=== MazeGuard Security ===
Contributors: designmazeus
Tags: security, two-factor, malware scanner, login security, file integrity
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 0.4.8
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Two-factor login, malware and file-integrity scanning, and account security, with findings that say what to do next.

== Description ==

MazeGuard Security protects the accounts that run your site and tells you, in plain terms, when its files or content change in ways worth a look.

Everything that runs on your own server is free and needs no account:

* **Two-factor authentication** with any TOTP app, a QR code drawn on your own server (the secret is never sent to a third-party QR service), and single-use recovery codes.
* **Account security** — an inventory of every account with risk flags (accounts without two-factor, dormant accounts, old passwords), a password policy that refuses recently used passwords, sign-in history with alerts for sign-ins from a network not seen before, and one-click session revocation.
* **Login protection** — throttling per address and per username, with correct client addresses behind Cloudflare and configured proxies.
* **File malware scanning** that separates where a file is from what it does. Each finding says whether the flagged code is the whole file or one part of a larger one, and what to do next: quarantine a payload file, reinstall an injected plugin or theme.
* **Comparison with WordPress.org** — installed plugins and themes are compared file by file with the release WordPress.org distributes for the same version. Files identical to the release are not reported as malware; PHP and JavaScript files that differ, and PHP files the release does not contain, are listed for review.
* **Database malware scanning** of posts and settings for stored scripts, hidden iframes and SEO spam, with the matched content shown on request and never copied into scan history.
* **File integrity** — WordPress core checked against WordPress.org checksums, a baseline of every file, uploads monitoring, and detection of mass changes such as a site's folders being reorganised at once.
* **Encrypted quarantine**, an activity log, scan history and CSV exports.
* **Cloudflare** — optionally, push blocks and Under Attack mode into your own Cloudflare zone with your own API token.

An optional MazeGuard Cloud licence adds what needs MazeGuard's servers: alert delivery from outside your site (so an alert still arrives when the site's own mail is broken), notice when the site stops checking in, and external vulnerability scans. Nothing is sent to MazeGuard Cloud unless you connect a licence and consent.

= What MazeGuard Security is not =

It is not a firewall and does not filter traffic. It does not remove malware automatically: findings are indicators for a person to review, not proof of infection, and nothing is changed or deleted without your say-so. Back up your site before quarantining or restoring files.

== External services ==

MazeGuard Security connects to the services below. Only the WordPress.org connections are made by default; every other one runs only after you configure it.

= WordPress.org (core checksums) =

Used to check WordPress core files against the official release, once a day and when you run an integrity check. Sends the WordPress version and locale to `api.wordpress.org`, with a user agent naming this plugin and your site address, as WordPress itself does when checking for updates. [Privacy](https://wordpress.org/about/privacy/)

= WordPress.org (plugin and theme releases) =

Used to compare installed plugins and themes with their official releases, once a day, when a malware scan has findings inside a plugin or theme, and when you click Compare now. Sends the slug and version of each installed plugin and theme to `downloads.wordpress.org`, which is the same information WordPress sends when checking for updates. Can be turned off under MazeGuard > Settings. [Privacy](https://wordpress.org/about/privacy/)

= MazeGuard Cloud =

Used only after you enter a licence key under MazeGuard > Cloud & License and tick the consent box. On connecting, it sends the licence key, site address and site name. It then sends a periodic summary: plugin, WordPress and PHP versions, security score, plugin and theme counts, and finding counts. Relayed alerts carry an event type, a severity and counts; the alert wording, which can name an account or address, is included only if you turn on that setting. File contents, database values, passwords and customer records are never sent. External scans, if requested, probe your site's public address from MazeGuard's servers. [Terms](https://getmazeguard.com/terms/) — [Privacy](https://getmazeguard.com/privacy/)

= Cloudflare =

Used only after you add a Cloudflare API token under MazeGuard > Settings. Sends requests to `api.cloudflare.com` to read your zone, create and remove IP blocks, purge the cache and change the security settings you choose. Blocks carry the address being blocked and a short note. [Terms](https://www.cloudflare.com/website-terms/) — [Privacy](https://www.cloudflare.com/privacypolicy/)

== Installation ==

1. Back up the site and its database.
2. Install MazeGuard Security from Plugins > Add New, or upload the ZIP, and activate it.
3. Open MazeGuard in the admin menu and run the WordPress core check and a file malware scan.
4. Review the findings, then build the file-integrity baseline. A baseline detects later changes; it does not prove that files already present are safe.
5. Set up two-factor authentication for your own account under Users > Profile.

== Frequently Asked Questions ==

= Is this a firewall? =

No. MazeGuard Security watches accounts, files and content and reports what changes. If you use Cloudflare, it can push blocks into your own Cloudflare zone, but the filtering is Cloudflare's.

= Will it delete files on its own? =

No. Nothing is quarantined, restored or deleted unless you choose to. Quarantine encrypts the file and can be undone.

= Where does MazeGuard store files? =

In `mazeguard-security/` inside your uploads folder: the list of files a running scan still has to read, and encrypted quarantine packages. The folder is protected from direct access, and quarantined files are encrypted, so a server that ignores the access rules still serves nothing readable. Everything else is kept in the database.

= Does it work alongside another security plugin or my host's malware scanner? =

Yes. It is designed to sit alongside a firewall or a host-level scanner. If your host cleans files automatically, those changes will appear in MazeGuard's file-integrity results for you to review.

= Why does a finding say a file "matches the WordPress.org release"? =

The file is byte-for-byte what WordPress.org distributes for that plugin or theme version, so it was not changed on your site. That does not mean the release itself is safe; it means the code is the author's.

== Screenshots ==

1. A malware finding says what the flagged code is, how sure the scanner is, and the next step: here, reinstalling the plugin rather than quarantining a file that also holds legitimate code.
2. The dashboard: security score, scanner results, hardening checks and WordPress core integrity at a glance.
3. Account security: every account with risk flags, sign-in history, password and session controls, and incident-response tools.
4. Two-factor setup with any authenticator app. The QR code is drawn on your own server.
5. The activity log, with severity filtering and CSV export.

== Changelog ==

= 0.4.8 =
* File integrity checks the result of an update, not only WordPress's announcement of it. A changed or added file byte-identical to the installed version's official release, a file that release no longer contains, and a WordPress core file matching core's checksums are all treated as expected, however the update arrived (host panel, WP-CLI, FTP). A file that differs from its release by one byte still reports.
* A plugin or theme folder removed outside WordPress is reported once, not as thousands of deleted files.
* Mass-change alerts are graded by what changed. Bulk changes to non-executable files outside the code folders, the shape of a cache or backup tool, are logged as a warning without an email; executable files, code folders and relocations stay critical; bulk media changes are high.
* Uploads folders with more than 50,000 files are compared folder by folder instead of being skipped.
* The activity log export follows the screen's severity filter, and failed logins can be hidden from both.

= 0.4.7 =
* Renamed from MazeGuard WP to MazeGuard Security.
* Installed plugins and themes are compared with their WordPress.org releases. Malware findings in files identical to the release are withheld and counted; PHP and JavaScript files that differ, and PHP files the release does not contain, are listed for review. Turn it off under Settings.
* Fixed: revoking every application password at once failed with a fatal error.
* Scan queues and quarantine moved from wp-content into `uploads/mazeguard-security/`; existing quarantined files move automatically. No PHP file is written into uploads.
* The two-factor challenge now ends the first-factor session using the token WordPress reports when it creates it, instead of reading the login cookie, which on a fresh sign-in did not yet hold that session.
* Settings no longer describe the scheduled database scan as a paid feature; it never was.
* WordPress coding-standards cleanup: database queries use placeholders for table names, file deletions go through wp_delete_file(), and each admin action verifies its own nonce. No behaviour change.

= 0.4.6 =
* Each file finding is measured as most of the file, inside a larger file, or location-only, labelled as running on the server or in visitors' browsers, and carries a next step to match: quarantine for a payload file, reinstall for an injected plugin, theme or core file, hand-editing for wp-config.php. The advice stays conditional, because measuring surrounding code cannot establish that it is genuine.
* Quarantining a file whose flagged code sits inside a larger file warns that the whole file, legitimate code included, will be removed.
* Database findings in Scan History can show the matched content, read live for that one finding and shown escaped. Scan history still stores only a hash; changed, deleted and no-longer-matching records are reported as such.
* Malware engine heuristic-v6. No detection rule changed, but cached verdicts lack the new fields, so the first scan after upgrading re-reads every file.

= 0.4.5 =
* WordPress core's own post embed — hidden by core until its secret handshake completes, and stored verbatim in the oEmbed cache — is no longer reported as a hidden-iframe injection. The exception requires every core marker including the browser-enforced sandbox attributes; a hidden iframe missing any of them still reports.

= 0.4.4 =
* The Google Tag Manager noscript snippet is no longer reported as a hidden-iframe injection. The exception covers only the exact GTM src; a lookalike domain or any other hidden iframe still reports.
* Database findings display what their location is (post type, status and title, or option name), so a finding in content without an edit screen can still be investigated. Applies to already-recorded findings.

= 0.4.3 =
* The database malware scan excludes transients and MazeGuard's own options in the query itself. A bloated options table previously forced the scan to page through tens of thousands of rows it would discard, with nothing visible moving on the screen.
* The database scan screen and live progress report rows examined alongside records analyzed, so a scan working through skipped rows can be told apart from a stuck one.
* A database-scan batch that repeatedly stops mid-analysis is skipped and logged with its row range instead of retried forever, so one problem row cannot wedge the scan.
* Scan History shows a running scan's progress as it goes, instead of 0 until completion.
* The database screen's findings card is labeled "Findings So Far This Scan" while a scan runs.

= 0.4.2 =
* Builder and optimizer cache directories (Divi's et-cache, LiteSpeed, WP Rocket's config) are no longer integrity-monitored. A cache purge previously read as a mass file deletion; rows already recorded under these directories are removed on upgrade.
* A protection placeholder in uploads (index.html, index.php, .htaccess, web.config) is no longer reported as changed when only its timestamp moves. Gravity Forms rewrites its whole set daily. A placeholder that changes size or disappears is still reported, and executable placeholders remain content-hashed by file integrity.
* An expected-change marker now lives until the next completed scan consumes it, instead of expiring after two hours. A plugin updated in the afternoon previously re-flagged as unexpected changes at the nightly scan.
* A plugin installed by uploading a ZIP over an existing copy is now named in the activity log and its files marked expected.
* The uploads census refuses to compare or adopt a directory walk that did not complete. A partial walk previously read as a mass deletion and corrupted the stored census so the next scan re-reported everything as added. An incomplete walk now costs one scan's uploads coverage and logs a warning saying so.
* Every scan records how many files each walker saw, so a swing in coverage is visible as itself instead of masquerading as deletions.
* Added, changed and deleted files are shown as separate lists and can be exported as CSV. They were previously merged into one preview cut at twenty lines, which hid the deleted list behind the changed one.
* A mass-change event now records example paths from the change set itself, so an exported log carries evidence.
* Added a one-step revoke of every application password on the site, on the Accounts screen. Application passwords authenticate the REST API without the login screen, so they survive a password sweep and are not covered by two-factor.

= 0.4.1 =
* A file-integrity scan now alerts on what it is the first to see, rather than on everything still standing against the baseline. The baseline deliberately does not adopt an unexpected change, so its list of differences is cumulative, and a site whose baseline had not been accepted was reported as a fresh bulk event every night with a number that grew each time.
* Uploads census results are no longer combined with cumulative file differences and described as a single scan; both halves now mean the same thing before they are combined.
* A file that has already been reported is reported again as soon as its contents change, so suppressing a repeat cannot hide an edit to something already in the list.
* A scan that finds nothing new while changes are still outstanding records that at information level and sends no alert, instead of logging a serious event every night.
* An alert reporting fewer changes than are outstanding in total now says how many are outstanding, so it cannot understate the backlog awaiting review.
* The mass-change alert took its total and its breakdown from two different change sets, so the figures in the message did not add up. Every figure now comes from the set the finding was measured on.

= 0.4.0 =
* Absence of a sign-in is now reported only for privileged accounts, and only once MazeGuard has been recording for at least thirty days. WordPress keeps no native last-login, so a shop previously reported nearly every account as a finding on day one and buried the few that mattered.
* Customer and subscriber accounts no longer produce findings above low severity. Not signing in is how a shop works; an administrator in the same state still reports at high severity.
* Admin notices no longer travel in the page URL. The message is held briefly against the signed-in user instead, so a crafted link cannot put words into a MazeGuard notice, notices appear once rather than on every refresh, and the address bar stays clean.
* The Nuclei Scanner API settings are hidden while a MazeGuard Cloud licence is connected, since external scans always use the licensed connection in that case.
* A plugin or theme installed through WordPress is now named in the activity log instead of being logged as "install completed: plugin", and its files are correctly recorded as an expected change.
* The File Malware and Database Malware screens now update while a scan is running, instead of showing whatever the last page load captured. Following a scan no longer means reloading the page repeatedly.
* A scan that has stopped is now reported as stopped rather than appearing to run forever. A batch ended by PHP's memory or time limit cannot schedule its own continuation, so a dead scan previously looked identical to a slow one.
* Progress is read from a new administrator-only endpoint that reports counts and timings only, never a file path, a finding or a database value.
* Character-code chains are now reported only when the assembled value reaches something that executes it. Chain length alone was flagging every PDF and font library on a site, so an ordinary WooCommerce store produced findings that were never malware. A chain that does reach an execution sink is now treated as strong evidence.
* An auto_prepend_file directive belonging to a security plugin's own request-time bootstrap, such as Wordfence extended protection or NinjaFirewall, is recognised and no longer reported on its own. A directive pointing anywhere else is still reported at high severity, because it runs on every request.
* Decoding a compressed or encoded value is only reported when the result is executed. Sliders, backup plugins and importers all store their own data this way, so the idiom alone said nothing about a file.
* Dynamic function execution now requires the variable being called to be the one holding request input. It previously matched any variable call near any request read, reporting ordinary plugin code as confirmed Critical.
* Hidden-iframe detection in stored content no longer matches ordinary embeds. It was matching inside marginwidth="0" and marginheight="0", which classic Google Maps and legacy embed codes carry, so any page with a map was reported as an injection. Genuinely hidden iframes are still reported.
* Bumped the analysis engine version, so files cached under the previous rules are re-analysed instead of returning their stored verdict.
* Added mass file-change detection: a bulk event is reported as one finding instead of thousands of integrity rows, and files moved into deeper directories with unchanged contents are called out specifically.
* Added monitoring of the uploads folder by path, size and modification time, so images and documents moved or removed in bulk are now visible. Contents are not read, so this shows that files moved — not that their contents are unchanged. Caches and image optimisers are skipped.
* Added optional Cloudflare edge blocking, pushing a locked-out address to your own Cloudflare zone so the next attempt is stopped before it reaches WordPress. MazeGuard removes the rules it created and never touches rules you created.
* Added Cloudflare incident controls: one-click Under Attack mode that restores your previous security level when turned off, cache purging, and advice on security level, Always Use HTTPS and minimum TLS version. Nothing is changed without being asked.
* Added a scannable QR code to two-factor enrolment, drawn on your own server because the code contains the shared secret. Manual key entry remains available.
* Added out-of-band alerting: alerts are relayed to MazeGuard Cloud and delivered from there by webhook or email, so they survive the site's own mail breaking during incident response.
* Added cloud-side silence detection, reporting a connected site that stops checking in and reporting again when it returns.
* Relayed alerts carry an event type, a severity and counts only. Including the alert wording is a separate opt-in, off by default.
* Alerts are queued and sent shortly after the event rather than during the request that raised them, so cloud delivery never delays a sign-in or a page load.
* Added TOTP two-factor authentication with authenticator-app enrolment, single-use recovery codes and per-role enforcement.
* Added a two-factor status column to the WordPress users list and a coverage summary in settings.
* Added an Accounts screen listing every account with last sign-in, originating address, open sessions, two-factor status and password age.
* Added risk findings for privileged accounts without two-factor, dormant accounts, stale passwords and sign-ins from many unrelated networks.
* Added per-account session revocation and administrator-initiated two-factor reset.
* Added a one-time site-wide password rotation sweep for incident response, plus per-account forced password change.
* Added minimum password length, username-in-password rejection and reuse blocking against previous passwords.
* Added optional age-based password rotation, disabled by default in line with current NIST guidance.
* Added per-user sign-in history and email alerts when a privileged account signs in from a network not seen before.
* Added CSV export of the account inventory for offboarding and access reviews.
* Added correct visitor-IP detection behind Cloudflare, load balancers and reverse proxies, with a trusted-proxy allowlist.
* Forwarded IP headers are now honoured only when the request arrives from a trusted range, so they cannot be spoofed to evade a lockout or forge log entries.
* Added a settings diagnostic that reports the detected client IP and warns about proxy misconfiguration before it causes a site-wide lockout.
* Added per-account failed-login throttling to catch credential stuffing spread across many source addresses.
* Removed the XML-RPC system.multicall method, which allowed hundreds of authentication attempts per request.
* Moved credential encryption off the WordPress salts onto a plugin-owned key, so rotating salts during incident response no longer orphans the cloud connection. Existing credentials migrate automatically on upgrade.

= 0.3.1 =
* Corrected malware scoring so a PHP file in uploads is only a weak location signal, not automatic Critical malware.
* Added confidence-aware severity: location-only indicators cannot become Critical.
* Ignored common harmless index.php placeholders such as “Silence is golden” and exit-only files.
* Added exact evidence-line snippets and transparent score explanations.
* Added cautious plugin, theme, Redux-related and WP STAGING-related source-context hints.
* Renamed “Trust this version” to “Mark expected” and clarified that this does not verify cleanliness.
* Added a persistent unchanged-file analysis cache to reduce repeat-scan file reads.
* Increased batch throughput and reduced the continuation delay.
* Added an upgrade warning when old-engine results are still displayed.
* Added a malware-cache database table and upgraded the database schema to version 4.

= 0.3.0 =
* Added persistent scan-history and finding tables.
* Added a batched WordPress database malware scanner for posts and options.
* Added PHP payload, hidden iframe, obfuscated JavaScript and SEO-spam database rules.
* Added opt-in MazeGuard Cloud registration and encrypted per-site credentials.
* Added HMAC-signed, timestamped and nonce-protected site requests.
* Added license status, plan feature and per-license site-limit support.
* Added daily cloud heartbeats containing summarized site-security status.
* Added commercial cloud external scans with DNS revalidation and site-bound jobs.
* Added a private license-management API and read-only agency dashboard backend.
* Added persistent scan history for file malware, database malware and external vulnerability scans.
* Added database findings to the security score and email summaries.
* Kept the legacy standalone Nuclei API path for controlled private deployments.

= 0.2.1 =
* Added hash-bound trusted-file exceptions for reviewed malware findings.
* Added faster persistent malware scan queues instead of repeatedly traversing the same files.
* Fixed quarantine checks on hosting environments that use symlinked WordPress paths.
* Separated WordPress-managed plugin, theme and core maintenance from unexpected file-integrity changes.
* Prevented incomplete integrity scans from reporting mass false deletions.
* Classified missing optional WordPress distribution files separately from required core files.
* Reduced duplicate activity events and added plugin/theme names to event titles.
* Rebuilt malware, quarantine and activity tables for mobile screens.
* Corrected security-score normalization.

= 0.2.0 =
* Added batched local malware scanning with weighted PHP behavior rules.
* Added encrypted quarantine, restore and permanent deletion.
* Added plugin, theme, user and important-setting activity logging.
* Added CSV activity export and daily or weekly email summaries.

= 0.1.0 =
* Initial Phase 1 development build.
