Skip to content

Developers

WordPress security hooks, constants and WP-CLI

Everything a developer can hook into in Stack Guard 4.0.0, read from the plugin source: 20 actions, 9 filters, 6 wp-config.php constants and the WP-CLI restore commands.

WordPress security hooks in Stack Guard

Each hook names the file and line it fires from in version 4.0.0. Open one for its parameters and an example.

Sign-in

  • smg_ip_locked_outactionFires when an IP address is locked out of signing in, either after too many failed attempts or because someone tried the username admin.
    ParameterTypeDescription
    $ipstringThe IP address that was locked out.
    $attempt_countintNumber of failed attempts. Always 1 when the reason is admin_username.
    $lockout_minutesintLength of the lockout in minutes.
    $usernamestringThe username that was tried.
    $reasonstringEither too_many_attempts or admin_username.
    add_action( 'smg_ip_locked_out', function ( $ip, $attempt_count, $lockout_minutes, $username, $reason ) {
        error_log( sprintf( 'Locked out %s for %d minutes (%s)', $ip, $lockout_minutes, $reason ) );
    }, 10, 5 );

    includes/class-smg-brute-force.php:275

  • smg_user_locked_outactionFires when a username is locked out after too many failed attempts against that account.
    ParameterTypeDescription
    $usernamestringThe username that was locked out.
    $attempt_countintNumber of failed attempts.
    $lockout_minutesintLength of the lockout in minutes.
    add_action( 'smg_user_locked_out', function ( $username, $attempt_count, $lockout_minutes ) {
        error_log( "Account {$username} locked for {$lockout_minutes} minutes" );
    }, 10, 3 );

    includes/class-smg-brute-force.php:301

  • smg_admin_username_blockedactionFires when a sign-in attempt using the username admin is blocked and the IP address is locked out.
    ParameterTypeDescription
    $ipstringThe IP address that made the attempt.
    $usernamestringThe username that was tried.
    add_action( 'smg_admin_username_blocked', function ( $ip, $username ) {
        error_log( "Blocked sign-in attempt for {$username} from {$ip}" );
    }, 10, 2 );

    includes/class-smg-brute-force.php:151

  • smg_multiple_failed_loginsactionFires once when failed sign-ins from one IP address reach the alert threshold within an hour, not on every failure after it.
    ParameterTypeDescription
    $ipstringThe IP address with the failed sign-ins.
    $countintFailed sign-ins counted in the last hour. Equal to the alert threshold.
    $usernamestringThe username of the attempt that reached the threshold, sanitized.
    add_action( 'smg_multiple_failed_logins', function ( $ip, $count, $username ) {
        error_log( "{$count} failed sign-ins from {$ip}" );
    }, 10, 3 );

    includes/class-smg-notifications.php:2413

  • smg_admin_loginactionFires after a completed sign-in by a user who can manage options. For accounts with two-factor, it fires after the second step, not after the password.
    ParameterTypeDescription
    $userWP_UserThe user who signed in.
    $ipstringThe client IP address.
    add_action( 'smg_admin_login', function ( $user, $ip ) {
        error_log( "Administrator {$user->user_login} signed in from {$ip}" );
    }, 10, 2 );

    includes/class-smg-notifications.php:2329

  • smg_new_device_loginactionFires when a user signs in from a browser and IP address combination not seen before. The very first sign-in after the alert is switched on only records the device and does not fire it.
    ParameterTypeDescription
    $userWP_UserThe user who signed in.
    $ipstringThe client IP address.
    $device_infostringA short description such as Chrome on Windows.
    add_action( 'smg_new_device_login', function ( $user, $ip, $device_info ) {
        error_log( "{$user->user_login} signed in from a new device: {$device_info}" );
    }, 10, 3 );

    includes/class-smg-notifications.php:2348

Two-factor

  • smg_2fa_enabledactionFires after a user turns on two-factor authentication.
    ParameterTypeDescription
    $user_idintThe user's ID.
    $methodstringThe method chosen: totp, email_code or magic_link.
    add_action( 'smg_2fa_enabled', function ( $user_id, $method ) {
        error_log( "User {$user_id} enabled two-factor ({$method})" );
    }, 10, 2 );

    includes/class-smg-2fa.php:1839

  • smg_2fa_disabledactionFires after two-factor is turned off for a user, either by the user with their password or by an administrator resetting another user.
    ParameterTypeDescription
    $user_idintThe ID of the user whose two-factor was turned off.
    add_action( 'smg_2fa_disabled', function ( $user_id ) {
        error_log( "Two-factor turned off for user {$user_id}" );
    } );

    includes/class-smg-2fa.php:1973

Firewall

  • smg_ban_addedactionFires when a new ban is added. It does not fire when an existing ban is refreshed.
    ParameterTypeDescription
    $typestringip, ip_range or user_agent.
    $valuestringWhat is banned.
    $reasonstringThe reason recorded for the ban.
    $expires_atstring|nullExpiry in UTC, or null for a permanent ban.
    add_action( 'smg_ban_added', function ( $type, $value, $reason, $expires_at ) {
        error_log( "Banned {$type} {$value}: {$reason}" );
    }, 10, 4 );

    includes/class-smg-firewall.php:423

  • smg_firewall_blockactionFires when a firewall rule blocks a request. It is limited to once per IP address and rule per hour, so it is an alert signal and not a request counter.
    ParameterTypeDescription
    $ipstringThe client IP address.
    $rule_namestringThe name of the rule that blocked the request.
    $uristringThe requested URI, sanitized and cut to 500 characters.
    add_action( 'smg_firewall_block', function ( $ip, $rule_name, $uri ) {
        error_log( "Rule {$rule_name} blocked {$ip} on {$uri}" );
    }, 10, 3 );

    includes/class-smg-firewall-rules.php:604

  • smg_404_threshold_exceededactionFires when an IP address passes the 404 limit and is banned for scanning the site for missing pages.
    ParameterTypeDescription
    $ipstringThe IP address that was banned.
    $countintNumber of 404 responses counted.
    add_action( 'smg_404_threshold_exceeded', function ( $ip, $count ) {
        error_log( "{$ip} hit {$count} missing pages and was banned" );
    }, 10, 2 );

    includes/class-smg-404-monitor.php:302

Scans

  • smg_scan_completeactionFires when a malware scan finishes, whether or not it found anything.
    ParameterTypeDescription
    $summaryarray{files_scanned: int, duration: string}Files scanned, and the duration as a translated, formatted string such as 12.3 seconds.
    add_action( 'smg_scan_complete', function ( $summary ) {
        error_log( "Scanned {$summary['files_scanned']} files" );
    } );

    includes/class-smg-malware-scanner.php:661

  • smg_malware_foundactionFires when a malware scan finishes with at least one issue.
    ParameterTypeDescription
    $summarystring[]Up to 10 lines, each like [CRITICAL] file.php: description.
    add_action( 'smg_malware_found', function ( $summary ) {
        error_log( "Malware scan found issues:\n" . implode( "\n", $summary ) );
    } );

    includes/class-smg-malware-scanner.php:671

  • smg_file_changes_detectedactionFires when a file change scan finds new, modified or deleted files and notifications are on for that scan.
    ParameterTypeDescription
    $changesarray{added: int, modified: int, deleted: int, files: string[]}Counts of each kind of change, and up to 10 files of each kind prefixed with + (new), ~ (modified) or - (deleted).
    add_action( 'smg_file_changes_detected', function ( $changes ) {
        error_log( "{$changes['added']} added, {$changes['modified']} modified, {$changes['deleted']} deleted" );
    } );

    includes/class-smg-file-monitor.php:284

  • smg_malware_signaturesfilterFilters the list of malware signatures when the scanner is created, so you can add your own patterns or remove ones that give false positives.
    ParameterTypeDescription
    $signaturesarray[]The built-in signatures. Each item is an array with name (string), pattern (a PCRE pattern including delimiters), severity (critical, high or medium) and description (string).

    Returns: array[] The signatures to scan with, in the same shape.

    add_filter( 'smg_malware_signatures', function ( $signatures ) {
        $signatures[] = array(
            'name'        => 'my_custom_backdoor',
            'pattern'     => '/evil_function\s*\(/i',
            'severity'    => 'high',
            'description' => 'Calls evil_function()',
        );
        return $signatures;
    } );

    includes/class-smg-malware-scanner.php:409

Users

  • smg_password_changedactionFires when a user's password changes, either from their profile screen or through a password reset link.
    ParameterTypeDescription
    $user_idintThe ID of the user whose password changed.
    add_action( 'smg_password_changed', function ( $user_id ) {
        error_log( "Password changed for user {$user_id}" );
    } );

    includes/class-smg-notifications.php:2426

  • smg_new_admin_createdactionFires when a new account that can manage options (an administrator) is registered.
    ParameterTypeDescription
    $user_idintThe ID of the new user.
    add_action( 'smg_new_admin_created', function ( $user_id ) {
        error_log( "New administrator created: user {$user_id}" );
    } );

    includes/class-smg-notifications.php:2448

  • smg_user_role_elevatedactionFires when an existing user who could not manage options is given a role that can. A brand new account fires smg_new_admin_created instead.
    ParameterTypeDescription
    $user_idintThe user's ID.
    $old_rolesstringThe previous roles, joined with a comma and a space.
    $new_rolestringThe new role.
    add_action( 'smg_user_role_elevated', function ( $user_id, $old_roles, $new_role ) {
        error_log( "User {$user_id} moved from {$old_roles} to {$new_role}" );
    }, 10, 3 );

    includes/class-smg-notifications.php:2477

  • smg_dormant_admin_daysfilterFilters how many days without a sign-in make an administrator count as dormant in the security checks. The minimum is 1.
    ParameterTypeDescription
    $daysintDays without a sign-in. Default 90.

    Returns: int Days.

    add_filter( 'smg_dormant_admin_days', function ( $days ) {
        return 60;
    } );

    includes/class-smg-admin.php:501

Backups

  • smg_backup_purge_cachesactionFires after a restore or a roll back changed the site, so you can clear caches Stack Guard does not know about, such as a host's own cache or a CDN plugin.
    ParameterTypeDescription
    $purgedstring[]Names of the caches Stack Guard already asked to purge.
    add_action( 'smg_backup_purge_caches', function ( $purged ) {
        if ( function_exists( 'my_host_flush_cache' ) ) {
            my_host_flush_cache();
        }
    } );

    includes/class-smg-backup-restore.php:757

  • smg_backup_loopback_timeoutfilterFilters how long, in seconds, a backup step waits when the site calls its own admin-ajax.php to start the next step. The minimum is 1.
    ParameterTypeDescription
    $secondsintTimeout in seconds. Default 5.

    Returns: int Seconds to wait.

    add_filter( 'smg_backup_loopback_timeout', function ( $seconds ) {
        return 15; // A slow host that answers its own requests late.
    } );

    includes/class-smg-backup-runner.php:277

  • smg_backup_use_db_lockfilterFilters whether the backup tick lock uses MySQL GET_LOCK. Return false on databases where named locks are unavailable or unreliable, and Stack Guard falls back to another lock.
    ParameterTypeDescription
    $useboolTrue to use GET_LOCK. Default true.

    Returns: bool Whether to use GET_LOCK.

    add_filter( 'smg_backup_use_db_lock', '__return_false' );

    includes/class-smg-backup-runner.php:375

  • smg_backup_free_spacefilterFilters the free disk space Stack Guard reports for the backup folder, used to warn before a backup, restore or upload that may not fit. Useful where disk_free_space() gives a wrong answer.
    ParameterTypeDescription
    $freeint|nullFree bytes, or null when unknown.

    Returns: int|null Free bytes, or null when unknown.

    add_filter( 'smg_backup_free_space', function ( $free ) {
        return null === $free ? 50 * GB_IN_BYTES : $free;
    } );

    includes/class-smg-backup-store.php:433

  • smg_backup_db_batch_rowsfilterFilters how many rows go into each INSERT statement when a table is backed up. Lower it on hosts with a small max_allowed_packet or little memory. The minimum is 1.
    ParameterTypeDescription
    $rowsintRows per batch, as worked out from the table's average row size.
    $tablestringThe table name.

    Returns: int Rows per batch.

    add_filter( 'smg_backup_db_batch_rows', function ( $rows, $table ) {
        return min( $rows, 200 );
    }, 10, 2 );

    includes/class-smg-backup-db-dumper.php:494

  • smg_backup_import_txn_bytesfilterFilters how many bytes of INSERT statements a restore runs in one database transaction. Lower it on hosts with a small transaction or packet limit. The minimum is 1.
    ParameterTypeDescription
    $bytesintBytes per transaction. Default 4194304 (4 MB).

    Returns: int Bytes per transaction.

    add_filter( 'smg_backup_import_txn_bytes', function ( $bytes ) {
        return 1048576; // 1 MB.
    } );

    includes/class-smg-backup-db-importer.php:88

Settings

  • smg_settings_updatedactionFires once per request after one or more Stack Guard settings were saved, with the names of all settings changed in that request.
    ParameterTypeDescription
    $namesstringThe changed setting (option) names, joined with a comma and a space.
    add_action( 'smg_settings_updated', function ( $names ) {
        error_log( "Stack Guard settings changed: {$names}" );
    } );

    includes/class-smg-activity-logger.php:569

Compatibility

  • smg_compat_environmentfilterFilters the facts the compatibility check reads about the site (server, constants, active plugins, themes and mail state) before it decides which warnings to show.
    ParameterTypeDescription
    $envarrayEnvironment facts. Keys include server, constants, is_wpe, abspath, home_host, plugins, themes, mail_hooks, mail_failure, events and now.

    Returns: array The environment facts, in the same shape.

    add_filter( 'smg_compat_environment', function ( $env ) {
        $env['plugins'][] = 'my-caching-plugin';
        return $env;
    } );

    includes/class-smg-compat.php:366

  • smg_compat_hostfilterFilters the detected hosting provider key used to choose host-specific advice. A value that is not a known host key is treated as no host.
    ParameterTypeDescription
    $hoststringThe detected host key, or an empty string when unknown.
    $envarrayThe environment facts, after the smg_compat_environment filter.

    Returns: string A known host key, or an empty string.

    add_filter( 'smg_compat_host', function ( $host, $env ) {
        return 'kinsta' === $host ? $host : '';
    }, 10, 2 );

    includes/class-smg-compat.php:407

wp-config.php constants

Add these above the line that says to stop editing. The recovery switches are how you get back in without touching the database; the recovery guide walks through it.

SMG_SAFE_MODEboolRecovery

Turns off everything that could keep you out: Login Guard, two-factor, lockouts, bans, country blocking, firewall rules and CAPTCHA. It also gives every administrator full access to Stack Guard, even one set to View Only or Hidden on the Access tab. A red notice appears on every admin page while it is on. Use it when you are not sure which protection is locking you out, then fix the cause and remove the line.

define( 'SMG_SAFE_MODE', true );
SMG_DISABLE_LOGIN_GUARDboolRecovery

Turns off the custom login address and the sign-in policy, so /wp-login.php works again. Use it when you have lost or forgotten the custom login address.

define( 'SMG_DISABLE_LOGIN_GUARD', true );
SMG_DISABLE_2FAboolRecovery

Turns off the two-factor step at sign-in. Each user's two-factor setup is kept, so it works again when you remove the line. Use it when a phone is lost or an authenticator no longer works.

define( 'SMG_DISABLE_2FA', true );
SMG_BACKUP_DIRstring (absolute path)

The folder Stack Guard keeps backups in. Without it, Stack Guard picks a randomly named folder beside the web root when it can write there, or inside wp-content. The constant is read when the folder is first chosen or when the saved folder is missing or not writable, so it does not move an existing, working backup folder. Several sites on one hosting account can share a folder. Uninstalling only removes the backups that this site's own records name.

define( 'SMG_BACKUP_DIR', '/home/example/stack-guard-backups' );
SMG_ENCRYPTION_KEYstring

Key material for encrypting two-factor app secrets. Without it, the key comes from AUTH_KEY (then SECURE_AUTH_KEY), so rotating the WordPress security keys forces the secrets to be re-encrypted. When this constant is defined, a key rotation leaves the secrets alone. Define it before people set up two-factor and keep it unchanged, because the encryption key is derived from this value.

define( 'SMG_ENCRYPTION_KEY', 'a-long-random-string-kept-only-in-wp-config' );
SMG_BACKUP_RELAY_URLstring (https URL)

The address of the sign-in relay used to connect Google Drive for backups. If wp-config.php does not set it, Stack Guard defines it as https://auth.wpstackguard.com. Set it only to point a test site at a test relay.

define( 'SMG_BACKUP_RELAY_URL', 'https://relay.example.test' );

WP-CLI commands

wp stack-guard restore status

Shows the restore that is waiting for Keep or Roll back, with its backup, when it finished waiting and any warnings. If nobody decides, the restore is kept automatically 7 days after it finished.

wp stack-guard restore status
wp stack-guard restore keep

Keeps the restored site and removes the old copy for good. It needs shell access to the server and no WordPress account, so it is the way to decide when a restore brought back users or a login address that leave nobody able to sign in. The decision is written to the activity log, and the cleanup runs in the command until it finishes or moves to the background.

  • --yes Do not ask for confirmation.
wp stack-guard restore keep --yes
wp stack-guard restore rollback

Rolls back: puts the site back exactly as it was before the restore, and removes the restored copy. Like keep, it needs shell access and no WordPress account, and the decision is written to the activity log.

  • --yes Do not ask for confirmation.
wp stack-guard restore rollback --yes

Questions

Stack Guard for developers: questions

Does Stack Guard have WordPress security hooks for developers?

Yes. Stack Guard 4.0.0 has 29 documented actions and filters for lockouts, two-factor, bans, scans, user changes, backups and the compatibility check.

How do I get back in if Stack Guard locks me out?

Add a recovery switch to wp-config.php, for example define( 'SMG_DISABLE_LOGIN_GUARD', true ); or define( 'SMG_SAFE_MODE', true );. Sign in, fix the setting, then remove the line.

Can I manage a restore from WP-CLI?

Yes. wp stack-guard restore status shows a restore waiting for a decision, and keep or rollback finishes it, which helps when the site cannot load in a browser.

Are these hooks stable?

Hooks listed here are the public ones. Hooks used only for tests or licensing are left off this page and may change without notice.

Build on a security plugin that documents itself

Free on every site. The hooks, constants and WP-CLI commands work on every plan.