diff --git a/changelog.txt b/changelog.txt index f4d2e68..9552271 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,25 @@ == Changelog == += 1.6.1 = + +_Release date: 2026-08-14_ + +**Fixed** + +* Fatal error during editor autosaves: the `[robotstxt_2fa_profile]` shortcode expanded when WordPress applied content filters to a post revision via REST (for example, an article that merely mentions the shortcode in its text). In that context `settings_errors()` and `submit_button()` are not defined, causing a fatal error and a failed autosave. The shortcode now bails out early on REST requests (avoiding side effects such as OTP secret generation) and loads the required wp-admin includes on demand elsewhere. + +**Compatibility** + +* WordPress: 6.4 – 7.1 +* PHP: 8.0 – 8.5 + +**Tests** + +* PHP Coding Standards: PHP_CodeSniffer / WPCS — 0 errors +* PHPStan: level 9 — 0 errors +* PHPCompatibility: 8.0–8.5 — 0 issues +* PHPUnit: 9.6.34 — 59 tests, 148 assertions + = 1.6.0 = _Release date: 2026-08-07_ diff --git a/includes/user/class-frontend-profile.php b/includes/user/class-frontend-profile.php index e7bb775..eb0b50d 100644 --- a/includes/user/class-frontend-profile.php +++ b/includes/user/class-frontend-profile.php @@ -97,6 +97,14 @@ class Frontend_Profile { * @return string */ public function render_shortcode( $atts ): string { + // Content filters (including shortcodes) also run on REST requests, + // e.g. when the editor autosaves a post that mentions the shortcode + // in its text. Rendering there is useless and has side effects + // (such as generating an OTP secret), so bail out early. + if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { + return ''; + } + if ( ! is_user_logged_in() ) { return ''; } @@ -123,6 +131,17 @@ class Frontend_Profile { return ''; } + // settings_errors() and submit_button() are defined in wp-admin + // includes that are not loaded on frontend or REST requests (the + // shortcode also renders when content filters run on REST autosaves). + // Load them on demand. + if ( ! function_exists( 'settings_errors' ) ) { + require_once ABSPATH . 'wp-admin/includes/misc.php'; + } + if ( ! function_exists( 'submit_button' ) ) { + require_once ABSPATH . 'wp-admin/includes/template.php'; + } + ob_start(); /** diff --git a/readme.txt b/readme.txt index a74bc1e..6eec4a0 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: security, two-factor authentication, login, otp Requires at least: 6.4 Tested up to: 7.0 Requires PHP: 8.0 -Stable tag: 1.6.0 +Stable tag: 1.6.1 License: GPLv3 or later License URI: https://www.gnu.org/licenses/gpl-3.0.html @@ -59,6 +59,14 @@ Yes. Activate the plugin at the network level. Network administrators can set an == Changelog == += 1.6.1 = + +_Release date: 2026-08-14_ + +**Fixed** + +* Fatal error during editor autosaves: the `[robotstxt_2fa_profile]` shortcode expanded when WordPress applied content filters to a post revision via REST (for example, an article that merely mentions the shortcode in its text). In that context the wp-admin render helpers are not loaded, causing a fatal error. The shortcode now bails out early on REST requests and loads the required wp-admin includes on demand elsewhere. + = 1.6.0 = _Release date: 2026-08-07_ @@ -90,66 +98,6 @@ _Release date: 2026-08-07_ * Security: the "Require 2FA to create Application Passwords" check could be bypassed via the `/wp/v2/users/me/application-passwords` REST route. The route matcher now accepts the `me` alias. -= 1.5.2 = - -_Release date: 2026-06-06_ - -**Added** - -* OTP secret import tool (2FA > Import) — detects authenticator app secrets from three supported plugins and lets administrators import them with one click, without users needing to reconfigure their authenticator app: - * **Two Factor** (community plugin) — reads `_two_factor_totp_key` user meta (plain Base32, direct import). - * **WP 2FA** (Melapress) — reads `wp_2fa_totp_key` user meta; decrypts if WP 2FA is still active, imports plain-text secrets otherwise. - * **Wordfence Login Security** — reads from the `wfls_2fa_secrets` database table (raw binary, Base32-encoded on import). -* Import skips users who already have OTP actively configured in our plugin. -* Admin notice banner when importable secrets are detected — shows count and link to import tool; dismissible per-admin for 7 days or permanently via a setting on the Import page. - -**Fixed** - -* Deactivating the Authenticator App from the profile no longer immediately pre-generates a new OTP secret. The secret is now deleted cleanly, and a fresh QR code is generated lazily on the next profile view. - -= 1.5.1 = - -_Release date: 2026-06-06_ - -**Security** - -* CSV exports (dashboard and WP-CLI) now use RFC 4180 encoding instead of `addslashes()` — fields with commas, double-quotes, or newlines are correctly quoted. -* GeoIP database path validated at read time with `is_file()` and `! is_link()` to prevent symlink traversal. -* `robotstxt_2fa_app_password_verification_window` filter return value validated as a positive integer before use. - -**Fixed** - -* Export CSV button is now only rendered to users with `manage_options` capability. - -= 1.5.0 = - -_Release date: 2026-06-05_ - -**Added** - -* Full audit user table — WP_List_Table with sortable columns (user, last verified), role and status filters, and one-click CSV export. -* GeoIP country restrictions — optional MaxMind GeoLite2 integration: per-country allow list (bypass 2FA), deny list (block login), always-challenge list (override frequency). Disabled when no database file is configured. -* Require 2FA before creating Application Passwords — REST endpoint rejects requests unless 2FA was completed within the last 15 minutes (configurable via `robotstxt_2fa_app_password_verification_window` filter). -* WP-CLI `wp 2fa export` — outputs a full CSV report. -* New developer filter `robotstxt_2fa_force_challenge` — override frequency-based skip and always require a fresh challenge. - -**Fixed** - -* Email digest cron now correctly reschedules when the frequency is changed between weekly and monthly. - -= 1.4.0 = - -_Release date: 2026-06-05_ - -**Added** - -* Audit Dashboard — top-level admin page with three summary cards (total users, 2FA-enabled count, recent failed attempts), a recent failed attempts table, and a 2FA status column in the Users list. -* Email notifications — four configurable event types: admin-enabled 2FA, login from unrecognised browser/IP, recovery code used (with optional admin copy), and a weekly or monthly activity digest for administrators. -* Application Passwords exemption — REST API and WP-CLI clients authenticated via Application Passwords skip the 2FA browser challenge by default. Configurable in Settings → Access control. -* IP allow list — IPs and CIDR ranges that bypass the 2FA challenge entirely (e.g. office networks). -* IP deny list — IPs and CIDR ranges that are blocked from logging in altogether. -* All IP matching supports both IPv4 and IPv6 CIDR notation. - = Previous versions = For the full changelog see the [changelog.txt](https://git.robotstxt.es/ROBOTSTXT/robotstxt-2fa/raw/branch/main/changelog.txt) file. diff --git a/robotstxt-2fa.php b/robotstxt-2fa.php index 49371d3..fcc4e89 100644 --- a/robotstxt-2fa.php +++ b/robotstxt-2fa.php @@ -3,7 +3,7 @@ * Plugin Name: 2FA (by ROBOTSTXT) * Plugin URI: https://www.robotstxt.es/plugins/robotstxt-2fa/ * Description: Adds two-factor authentication to the WordPress login flow. - * Version: 1.6.0 + * Version: 1.6.1 * Author: ROBOTSTXT * Author URI: https://www.robotstxt.es/ * Text Domain: robotstxt-2fa @@ -23,7 +23,7 @@ if ( ! defined( 'ABSPATH' ) ) { } if ( ! defined( 'ROBOTSTXT_2FA_VERSION' ) ) { - define( 'ROBOTSTXT_2FA_VERSION', '1.6.0' ); + define( 'ROBOTSTXT_2FA_VERSION', '1.6.1' ); } if ( ! defined( 'ROBOTSTXT_2FA_FILE' ) ) { diff --git a/update.json b/update.json index 02ed343..e46dbb3 100644 --- a/update.json +++ b/update.json @@ -1,20 +1,20 @@ { "name": "2FA (by ROBOTSTXT)", "slug": "robotstxt-2fa", - "version": "1.6.0", - "download_url": "https://git.robotstxt.es/ROBOTSTXT/robotstxt-2fa/releases/download/1.6.0/robotstxt-2fa-1.6.0.zip", + "version": "1.6.1", + "download_url": "https://git.robotstxt.es/ROBOTSTXT/robotstxt-2fa/releases/download/1.6.1/robotstxt-2fa-1.6.1.zip", "requires": "6.4", "requires_php": "8.0", "tested": "7.1", - "last_updated": "2026-08-07", + "last_updated": "2026-08-14", "author": "ROBOTSTXT", "author_profile": "https://www.robotstxt.es/", "homepage": "https://www.robotstxt.es/plugins/robotstxt-2fa/", "description": "Adds per-role two-factor authentication to the WordPress login flow. Supports email codes, authenticator apps (TOTP), and recovery codes.", - "changelog": "

1.6.0 - 2026-08-07

", + "changelog": "

1.6.1 - 2026-08-14

", "sections": { "description": "Adds per-role two-factor authentication to the WordPress login flow. Supports email codes, authenticator apps (TOTP), and recovery codes.", - "changelog": "

1.6.0 - 2026-08-07

" + "changelog": "

1.6.1 - 2026-08-14

" }, "banners": { "low": "", "high": "" }, "icons": { "1x": "", "2x": "" }