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>
159 lines
9.6 KiB
PHP
159 lines
9.6 KiB
PHP
<?php
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
$argus_active_page = 'overview';
|
|
include ARGUS_WPD_DIR . 'admin/views/partials/header.php';
|
|
|
|
$range_labels = array( '24h' => '24H', '7d' => '7D', '30d' => '30D' );
|
|
|
|
$score = max( 0, 100 - ( $counts['critical'] * 15 ) - ( $counts['high'] * 8 ) - ( $counts['medium'] * 3 ) - ( $counts['low'] * 1 ) );
|
|
$score_r = 54;
|
|
$score_circ = 2 * M_PI * $score_r;
|
|
$score_offset = $score_circ * ( 1 - $score / 100 );
|
|
?>
|
|
|
|
<h1 class="argus-wpd-page-title"><?php esc_html_e( 'ARGUS Defence', 'argus-wordpress-defence' ); ?></h1>
|
|
<p style="color:var(--muted);font-size:13px;margin:0 0 24px;"><?php esc_html_e( 'Security Operations Center for your WordPress site.', 'argus-wordpress-defence' ); ?></p>
|
|
|
|
<?php if ( 'installed' !== $mu_status ) : ?>
|
|
<div class="notice notice-warning" style="padding:14px 18px;margin:0 0 20px;">
|
|
<p>
|
|
<?php
|
|
printf(
|
|
|
|
esc_html__( 'Maximum protection is not fully active on this host (status: %s). ARGUS is still protecting your site via its standard fallback hook, but consider checking file permissions on wp-content/mu-plugins.', 'argus-wordpress-defence' ),
|
|
esc_html( $mu_status )
|
|
);
|
|
?>
|
|
</p>
|
|
</div>
|
|
<?php elseif ( 'stale' === $mu_exec_status ) : ?>
|
|
<div class="notice notice-error" style="padding:14px 18px;margin:0 0 20px;">
|
|
<p><?php esc_html_e( 'Maximum protection was installed but ARGUS has not seen it actually run recently -- it may have been removed from wp-content/mu-plugins outside of WordPress. ARGUS is still protecting your site via its standard fallback hook, but check that wp-content/mu-plugins/argus-mu-core.php still exists.', 'argus-wordpress-defence' ); ?></p>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ( null !== $license_summary ) : ?>
|
|
<div class="argus-wpd-panel" style="display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;">
|
|
<div>
|
|
<?php if ( 'licensed' === $license_summary['status'] ) : ?>
|
|
<span class="argus-wpd-status-pill" style="color:var(--emerald);border-color:rgba(63,185,80,.35);"><?php esc_html_e( 'LICENSED', 'argus-wordpress-defence' ); ?></span>
|
|
<span style="margin-left:10px;color:var(--faint);font-size:12.5px;"><?php esc_html_e( 'Premium features are fully active.', 'argus-wordpress-defence' ); ?></span>
|
|
<?php elseif ( 'trial' === $license_summary['status'] ) : ?>
|
|
<span class="argus-wpd-status-pill" style="color:var(--amber);border-color:rgba(255,170,0,.35);"><?php esc_html_e( 'TRIAL', 'argus-wordpress-defence' ); ?></span>
|
|
<span style="margin-left:10px;color:var(--faint);font-size:12.5px;">
|
|
<?php
|
|
printf(
|
|
/* translators: %d: number of days remaining in the free trial */
|
|
esc_html( _n( '%d day left in your free trial -- everything is active.', '%d days left in your free trial -- everything is active.', $license_summary['days_remaining'], 'argus-wordpress-defence' ) ),
|
|
(int) $license_summary['days_remaining']
|
|
);
|
|
?>
|
|
</span>
|
|
<?php else : ?>
|
|
<span class="argus-wpd-status-pill" style="color:var(--rose);border-color:rgba(230,103,103,.35);"><?php esc_html_e( 'TRIAL ENDED', 'argus-wordpress-defence' ); ?></span>
|
|
<span style="margin-left:10px;color:var(--faint);font-size:12.5px;"><?php esc_html_e( 'Your 30-day free trial has ended. ARGUS keeps protecting your site -- enter a license key to keep Premium features and support.', 'argus-wordpress-defence' ); ?></span>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php if ( 'licensed' !== $license_summary['status'] ) : ?>
|
|
<a class="button button-primary" href="<?php echo esc_url( $license_summary['purchase_url'] ? $license_summary['purchase_url'] : admin_url( 'admin.php?page=argus-wpd-settings' ) ); ?>"><?php echo $license_summary['purchase_url'] ? esc_html__( 'Buy a license', 'argus-wordpress-defence' ) : esc_html__( 'Enter license key', 'argus-wordpress-defence' ); ?></a>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="argus-wpd-grid">
|
|
<div class="argus-wpd-card argus-wpd-score">
|
|
<svg viewBox="0 0 128 128">
|
|
<circle class="argus-wpd-score-track" cx="64" cy="64" r="<?php echo esc_attr( $score_r ); ?>"></circle>
|
|
<circle class="argus-wpd-score-arc" cx="64" cy="64" r="<?php echo esc_attr( $score_r ); ?>"
|
|
stroke-dasharray="<?php echo esc_attr( $score_circ ); ?>"
|
|
stroke-dashoffset="<?php echo esc_attr( $score_offset ); ?>"
|
|
transform="rotate(-90 64 64)"></circle>
|
|
<text x="64" y="70" text-anchor="middle" class="argus-wpd-score-num"><?php echo esc_html( $score ); ?></text>
|
|
</svg>
|
|
<div class="argus-wpd-score-label"><?php esc_html_e( 'Security Score', 'argus-wordpress-defence' ); ?></div>
|
|
</div>
|
|
|
|
<div class="argus-wpd-metric-card">
|
|
<div class="argus-wpd-metric-label"><?php esc_html_e( 'Threats Blocked Today', 'argus-wordpress-defence' ); ?></div>
|
|
<div class="argus-wpd-metric-value"><?php echo esc_html( number_format_i18n( $blocked_today ) ); ?></div>
|
|
<div class="argus-wpd-metric-sub"><?php esc_html_e( 'Local WAF + policy engine, since 00:00 UTC', 'argus-wordpress-defence' ); ?></div>
|
|
</div>
|
|
<div class="argus-wpd-metric-card">
|
|
<div class="argus-wpd-metric-label"><?php esc_html_e( 'Malicious IPs', 'argus-wordpress-defence' ); ?></div>
|
|
<div class="argus-wpd-metric-value"><?php echo esc_html( number_format_i18n( $malicious_ips ) ); ?></div>
|
|
<div class="argus-wpd-metric-sub"><?php esc_html_e( 'Currently banned by local WAF + policy engine', 'argus-wordpress-defence' ); ?></div>
|
|
</div>
|
|
<div class="argus-wpd-metric-card">
|
|
<div class="argus-wpd-metric-label"><?php esc_html_e( 'Blocked Requests', 'argus-wordpress-defence' ); ?></div>
|
|
<div class="argus-wpd-metric-value"><?php echo esc_html( number_format_i18n( $blocked_30d ) ); ?></div>
|
|
<div class="argus-wpd-metric-sub"><?php esc_html_e( 'Last 30 days', 'argus-wordpress-defence' ); ?></div>
|
|
</div>
|
|
<div class="argus-wpd-metric-card">
|
|
<div class="argus-wpd-metric-label"><?php esc_html_e( 'Security Events', 'argus-wordpress-defence' ); ?></div>
|
|
<div class="argus-wpd-metric-value"><?php echo esc_html( number_format_i18n( $security_events ) ); ?></div>
|
|
<div class="argus-wpd-metric-sub"><?php esc_html_e( 'Medium severity or higher, last 30 days', 'argus-wordpress-defence' ); ?></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="argus-wpd-metric-grid argus-wpd-metric-grid-2">
|
|
<div class="argus-wpd-metric-card">
|
|
<div class="argus-wpd-metric-label"><?php esc_html_e( 'IPs Tracked', 'argus-wordpress-defence' ); ?></div>
|
|
<div class="argus-wpd-metric-value"><?php echo esc_html( number_format_i18n( $ips_tracked ) ); ?></div>
|
|
<div class="argus-wpd-metric-sub"><?php esc_html_e( 'Total IPs with threat intelligence on file, local + ANIS', 'argus-wordpress-defence' ); ?></div>
|
|
</div>
|
|
<div class="argus-wpd-metric-card">
|
|
<div class="argus-wpd-metric-label"><?php esc_html_e( 'ANIS Protections', 'argus-wordpress-defence' ); ?></div>
|
|
<div class="argus-wpd-metric-value"><?php echo esc_html( number_format_i18n( $anis_protections ) ); ?></div>
|
|
<div class="argus-wpd-metric-sub"><?php esc_html_e( 'Blocks powered by ARGUS global threat intelligence', 'argus-wordpress-defence' ); ?></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="argus-wpd-area-card">
|
|
<div class="argus-wpd-area-head">
|
|
<h2 style="margin:0"><?php esc_html_e( 'Blocked Requests', 'argus-wordpress-defence' ); ?> · <?php echo esc_html( $range_labels[ $range ] ?? $range ); ?></h2>
|
|
<div class="argus-wpd-range-tabs">
|
|
<?php foreach ( $range_labels as $key => $label ) : ?>
|
|
<a href="<?php echo esc_url( add_query_arg( 'range', $key ) ); ?>" class="<?php echo $key === $range ? 'active' : ''; ?>"><?php echo esc_html( $label ); ?></a>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
<?php if ( array_sum( wp_list_pluck( $series, 'value' ) ) <= 0 ) : ?>
|
|
<p class="argus-wpd-area-empty"><?php esc_html_e( 'No blocked requests recorded in this window yet.', 'argus-wordpress-defence' ); ?></p>
|
|
<?php else : ?>
|
|
<?php echo Argus_Charts::area_chart( $series ); // phpcs:ignore WordPress.Security.EscapeOutput ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="argus-wpd-status-row">
|
|
<div class="argus-wpd-status-chip <?php echo Argus_Settings::get( 'waf_enabled', true ) ? ( Argus_Settings::is_monitor_only() ? 'chip-amber' : 'chip-cyan' ) : 'chip-off'; ?>">
|
|
<span class="dot"></span>
|
|
<span class="label">FIREWALL</span>
|
|
<span class="state"><?php echo Argus_Settings::get( 'waf_enabled', true ) ? ( Argus_Settings::is_monitor_only() ? esc_html__( 'MONITORING', 'argus-wordpress-defence' ) : esc_html__( 'BLOCKING', 'argus-wordpress-defence' ) ) : esc_html__( 'DISABLED', 'argus-wordpress-defence' ); ?></span>
|
|
</div>
|
|
<div class="argus-wpd-status-chip <?php echo ( $malware_open > 0 || $integrity_open > 0 ) ? 'chip-rose' : 'chip-emerald'; ?>">
|
|
<span class="dot"></span>
|
|
<span class="label">SCANNER</span>
|
|
<span class="state"><?php echo ( $malware_open > 0 || $integrity_open > 0 ) ? esc_html__( 'ISSUES FOUND', 'argus-wordpress-defence' ) : esc_html__( 'CLEAN', 'argus-wordpress-defence' ); ?></span>
|
|
</div>
|
|
<?php
|
|
$anis_chip_map = array(
|
|
'active' => array( 'chip-cyan', __( 'CONNECTED', 'argus-wordpress-defence' ) ),
|
|
'limited' => array( 'chip-amber', __( 'LIMITED', 'argus-wordpress-defence' ) ),
|
|
'not_connected' => array( 'chip-off', __( 'CONNECTING…', 'argus-wordpress-defence' ) ),
|
|
'disabled' => array( 'chip-off', __( 'NOT CONNECTED', 'argus-wordpress-defence' ) ),
|
|
);
|
|
list( $anis_chip_class, $anis_chip_state ) = $anis_chip_map[ $anis_status['protection'] ] ?? $anis_chip_map['not_connected'];
|
|
?>
|
|
<div class="argus-wpd-status-chip <?php echo esc_attr( $anis_chip_class ); ?>">
|
|
<span class="dot"></span>
|
|
<span class="label">ANIS</span>
|
|
<span class="state"><?php echo esc_html( $anis_chip_state ); ?></span>
|
|
</div>
|
|
</div>
|
|
|
|
<?php include ARGUS_WPD_DIR . 'admin/views/partials/footer.php'; ?>
|