ARGUS WordPress Defence 7.23.0

Real changes since 1.0.0, all live-verified before this release:

- Firewall: rule corpus expanded 19 -> 43 rules, real OWASP-CRS-equivalent
  coverage (XXE, SSRF, session fixation, Log4Shell/JNDI, scanner-tool
  detection, deeper SQL injection/XSS/PHP-injection).
- Fixed a real bug: a quarantined file's severity badge and its content
  analysis score could disagree with no explanation (e.g. a benign file
  showing CRITICAL next to Score 0); both are now derived consistently
  and shown together.
- ARGUS now always keeps itself updated, and can optionally do the same
  for every other installed plugin and theme (Settings, on by default) --
  uses WordPress's own native update system, nothing custom.
- Global Threat Intelligence is now opt-in, not automatic -- a single
  click on its own page, with an honest, specific description of exactly
  what's shared (an IP address, a reason code, a confidence score, a
  country). Previously connected automatically on activation.
- New first-run Welcome screen after activation: confirms what's already
  protecting the site, and surfaces the few real optional choices in one
  place.
- Dashboard: running version now visible in the header; new "IPs
  Tracked" and "ANIS Protections" metrics.
- Full WordPress.org Plugin Directory readiness audit performed against
  this codebase. Two real compliance issues found and fixed (see above:
  Global Threat Intelligence's default, and the self-update mechanism,
  which is excluded from this build entirely -- WordPress.org prohibits
  a plugin from using any update channel other than its own, even an
  inert one). This release is still self-distributed, not a WordPress.org
  submission -- that remains a future step.

Verified before publishing: this exact ZIP was installed, activated (14
admin pages loaded clean, zero PHP errors/warnings), and uninstalled
(zero leftover database tables or options) in a fresh, disposable
WordPress + MySQL environment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
ARGUS
2026-08-09 19:08:02 +00:00
co-authored by Claude Sonnet 5
parent 35742e05ab
commit 35efd3e485
21 changed files with 632 additions and 326 deletions
+99 -1
View File
@@ -14,6 +14,28 @@ class Argus_Admin {
add_action( 'admin_post_argus_wpd_download_backup', array( __CLASS__, 'handle_backup_download' ) );
add_action( 'admin_init', array( __CLASS__, 'handle_firewall_actions' ) );
add_action( 'admin_init', array( __CLASS__, 'maybe_redirect_to_welcome' ) );
}
// Standard WordPress plugin convention: redirect to a one-time Welcome
// screen right after activation, but never on a bulk-activate or a
// multisite network-wide activation (both would otherwise hijack
// whichever admin page the user was actually trying to reach).
public static function maybe_redirect_to_welcome() {
if ( ! get_transient( 'argus_wpd_do_activation_redirect' ) ) {
return;
}
delete_transient( 'argus_wpd_do_activation_redirect' );
if ( wp_doing_ajax() || is_network_admin() || isset( $_GET['activate-multi'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
return;
}
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
wp_safe_redirect( admin_url( 'admin.php?page=argus-wpd-welcome' ) );
exit;
}
public static function handle_firewall_actions() {
@@ -84,10 +106,57 @@ class Argus_Admin {
add_submenu_page( 'argus-wpd-dashboard', __( 'ANIS', 'argus-wordpress-defence' ), __( 'ANIS', 'argus-wordpress-defence' ), 'manage_options', 'argus-wpd-anis', array( __CLASS__, 'render_anis' ) );
add_submenu_page( 'argus-wpd-dashboard', __( 'Audit Log', 'argus-wordpress-defence' ), __( 'Audit Log', 'argus-wordpress-defence' ), 'manage_options', 'argus-wpd-audit-log', array( __CLASS__, 'render_audit_log' ) );
add_submenu_page( 'argus-wpd-dashboard', __( 'Settings', 'argus-wordpress-defence' ), __( 'Settings', 'argus-wordpress-defence' ), 'manage_options', 'argus-wpd-settings', array( __CLASS__, 'render_settings' ) );
// Reachable at ?page=argus-wpd-welcome right after activation, but
// CSS-hidden from the sidebar (hide_settings_from_sidebar(), same
// mechanism already used for Settings) -- it's a onboarding screen,
// not a place to keep coming back to via the nav. Registering with a
// real parent rather than null: passing null as parent_slug left
// get_admin_page_title() unable to resolve a title in this WP
// version, cascading into a "headers already sent" fatal-adjacent
// warning -- confirmed live, not theoretical.
add_submenu_page( 'argus-wpd-dashboard', __( 'Welcome to ARGUS Defence', 'argus-wordpress-defence' ), __( 'Welcome', 'argus-wordpress-defence' ), 'manage_options', 'argus-wpd-welcome', array( __CLASS__, 'render_welcome' ) );
}
public static function render_welcome() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'You do not have permission to do this.', 'argus-wordpress-defence' ) );
}
$template_applied = null;
if ( isset( $_POST['argus_wpd_apply_template_nonce'], $_POST['template'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['argus_wpd_apply_template_nonce'] ) ), 'argus_wpd_apply_template' ) ) { // phpcs:ignore
$key = sanitize_key( wp_unslash( $_POST['template'] ) ); // phpcs:ignore
if ( class_exists( 'Argus_Templates' ) && Argus_Templates::apply( $key ) ) {
$templates = Argus_Templates::all();
$template_applied = $templates[ $key ]['label'];
}
}
if ( isset( $_POST['argus_wpd_anis_toggle_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['argus_wpd_anis_toggle_nonce'] ) ), 'argus_wpd_anis_toggle' ) ) { // phpcs:ignore
$action = sanitize_key( wp_unslash( $_POST['anis_action'] ?? '' ) ); // phpcs:ignore
$user = wp_get_current_user();
if ( 'enable' === $action ) {
Argus_Settings::update( array( 'anis_enabled' => true ) );
Argus_ANIS_Client::register();
Argus_Events::record( 'anis_enabled', 'info', sprintf( 'ARGUS Cloud connection enabled by %s', $user->user_login ), array( 'actor' => $user->user_login ) );
}
}
if ( isset( $_POST['argus_wpd_auto_update_all_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['argus_wpd_auto_update_all_nonce'] ) ), 'argus_wpd_save_auto_update_all' ) ) { // phpcs:ignore
Argus_Settings::update( array( 'auto_update_all_enabled' => ! empty( $_POST['auto_update_all_enabled'] ) ) ); // phpcs:ignore
}
$settings = Argus_Settings::all();
$templates = class_exists( 'Argus_Templates' ) ? Argus_Templates::all() : array();
$active_template = class_exists( 'Argus_Templates' ) ? Argus_Templates::active() : '';
$recommended_template = class_exists( 'Argus_Templates' ) ? Argus_Templates::recommended() : null;
$anis_status = Argus_ANIS_Client::status();
include ARGUS_WPD_DIR . 'admin/views/welcome.php';
}
public static function hide_settings_from_sidebar() {
echo '<style>#adminmenu .wp-submenu a[href$="page=argus-wpd-settings"]{display:none}</style>';
echo '<style>#adminmenu .wp-submenu a[href$="page=argus-wpd-settings"],#adminmenu .wp-submenu a[href$="page=argus-wpd-welcome"]{display:none}</style>';
}
public static function enqueue_assets( $hook ) {
@@ -121,6 +190,15 @@ class Argus_Admin {
$integrity_open = Argus_Findings::count_open( 'integrity' );
$anis_status = Argus_ANIS_Client::status();
$ips_tracked = Argus_ANIS_Client::local_reputation_count();
$anis_protections = Argus_ANIS_Client::blocked_count();
// Argus_License exists only in the separately-distributed Premium
// build (see includes/class-argus-license.php's own header) -- the
// free WordPress.org build has no trial/license concept at all, and
// this stays null there so overview.php shows nothing for it.
$license_summary = class_exists( 'Argus_License' ) ? Argus_License::summary() : null;
include ARGUS_WPD_DIR . 'admin/views/overview.php';
}
@@ -530,6 +608,21 @@ class Argus_Admin {
}
public static function render_anis() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'You do not have permission to do this.', 'argus-wordpress-defence' ) );
}
if ( isset( $_POST['argus_wpd_anis_toggle_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['argus_wpd_anis_toggle_nonce'] ) ), 'argus_wpd_anis_toggle' ) ) { // phpcs:ignore
$action = sanitize_key( wp_unslash( $_POST['anis_action'] ?? '' ) ); // phpcs:ignore
$user = wp_get_current_user();
if ( 'enable' === $action ) {
Argus_Settings::update( array( 'anis_enabled' => true ) );
Argus_ANIS_Client::register();
Argus_Events::record( 'anis_enabled', 'info', sprintf( 'ARGUS Cloud connection enabled by %s', $user->user_login ), array( 'actor' => $user->user_login ) );
} elseif ( 'disable' === $action ) {
Argus_Settings::update( array( 'anis_enabled' => false ) );
Argus_Events::record( 'anis_disabled', 'info', sprintf( 'ARGUS Cloud connection disabled by %s', $user->user_login ), array( 'actor' => $user->user_login ) );
}
}
$anis_status = Argus_ANIS_Client::status();
include ARGUS_WPD_DIR . 'admin/views/anis.php';
}
@@ -582,6 +675,11 @@ class Argus_Admin {
Argus_Settings::remove_exception( 'ip', sanitize_text_field( wp_unslash( $_POST['exception_value'] ) ) ); // phpcs:ignore
}
if ( isset( $_POST['argus_wpd_auto_update_all_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['argus_wpd_auto_update_all_nonce'] ) ), 'argus_wpd_save_auto_update_all' ) ) { // phpcs:ignore
Argus_Settings::update( array( 'auto_update_all_enabled' => ! empty( $_POST['auto_update_all_enabled'] ) ) ); // phpcs:ignore
$saved = true;
}
$update_check_result = null;
if ( isset( $_POST['argus_wpd_check_updates_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['argus_wpd_check_updates_nonce'] ) ), 'argus_wpd_check_updates' ) && class_exists( 'Argus_Update_Client' ) ) { // phpcs:ignore
$update_check_result = Argus_Update_Client::check_now();