=== Ensomedia Security powered by shieldwave.io ===
Contributors: shieldwave
Tags: security, malware scanner, file integrity, vulnerability scanner, hardening
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.1.18
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Finds malware, tampered files, known vulnerabilities, injected content and risky settings, and explains each fix in plain words.

== Description ==

[shieldwave.io](https://shieldwave.io/?utm_source=wordpress&utm_medium=plugin&utm_campaign=readme) by [ensomedia.dev](https://ensomedia.dev)

Ensomedia Security, powered by shieldwave.io, scans your site from the inside and tells you in plain language what is wrong and how to fix it. It is built around one idea: **an alert has to be worth your attention**. Every check, the scheduled scans and the email alerts are free, work without an account and have no limits.

= Why owners switch to it =

* **Official checksums first.** Core, directory plugins and directory themes are compared with the files WordPress.org published for your version.
* **Malware detection that needs real evidence.** One weak signal, such as base64 in a file, is never a finding. Findings the scanner is not sure about go under "Ask your developer to check" and never send email.
* **Change monitoring for what WordPress.org cannot verify.** Premium plugins, custom themes, must-use plugins and drop-ins get a baseline; a new PHP file that appears without an update is reported.
* **Alerts that do not cry wolf.** One email per scan, only for problems that are new or got worse at or above your threshold, at most four a day; at once when an account becomes administrator or the user name of an administrator gets locked; a weekly summary if you want one. A check that could not run never sends email and never marks anything as fixed.
* **Light on your server.** Scans run in the background in short steps, and unchanged files are not analyzed again.
* **Never rewrites your site on its own.** Files, users and settings change only when you press a button or turn on a switch.

= The checks =

* **Malware and backdoors** in files WordPress.org cannot vouch for: web shells, decode-and-execute chains, obfuscated code, remote code loaders, PHP hidden in images, spam mailers, hidden administrators, injected JavaScript and malicious redirects.
* **Core, plugin and theme files** against the WordPress.org checksums or release packages of your versions, plus PHP files those releases do not contain.
* **File changes** against the recorded baseline, and plugin or theme folders that appear without the WordPress installer.
* **PHP files in uploads**, and .htaccess or .user.ini rules that make the server run uploads as PHP.
* **Known vulnerabilities** (opt-in) in plugins, themes and WordPress, premium ones included, with the version that fixes each one.
* **Closed, abandoned and unused plugins and themes**, **updates** and **PHP version** support.
* **Injected content**: scripts, hidden iframes and spam links in posts, widgets and options. Ordinary embeds and tracking codes are left alone.
* **Exposed files** that your web server hands out to anyone (copies of wp-config.php, database dumps, backups, .git folders, .env files, logs), confirmed by requesting them from your own site; leftover installers and database tools; folder listings.
* **Accounts and configuration**: administrators, user registration, wp-config.php, debug output, file editor, XML-RPC, HTTPS, user list exposure and suspicious scheduled tasks.

Each issue has a severity, what it means, how to fix it and the files, plugins or settings involved. Problems of the same kind are grouped. An ignored file comes back if it changes again.

Live protection (three features, all opt-in):

* **Known vulnerabilities**: the lookup above.
* **Live threat feed**: new malware rules and file signatures from ShieldWave reach your site within about an hour, verified by signature. It sends nothing about your site.
* **AI second opinion**: for a file the local rules cannot judge, a redacted excerpt gets a verdict in plain words.

All three are off until you turn them on and run from WP-Cron once an hour, whether or not a scan is running. See External services.

= Login protection =

* Failed logins are limited per address (five by default, then a cooldown you set) and per user name across all addresses (20 an hour by default, any number from 1 to 500, 0 turns it off). Password reset requests are limited per address, 5 an hour by default. A lock is always temporary, and trusted addresses are never locked.
* Only the connection's own address counts, never a header a request can fake. Behind a proxy or CDN, list its addresses or CIDR ranges (/8 or narrower, up to 20 entries) under trusted proxies and choose the header that carries the visitor's address.
* User names stay private: the login form, ?author=N, the REST API user list, embeds and the users sitemap stop giving them to visitors who are not logged in.
* On by default for new installs, off after an upgrade until you turn it on.

= Two-factor login, passkeys and sessions =

* Two-factor login with an authenticator app, ten recovery codes and `wp shieldwave two-factor disable <user>`, so nobody is locked out. Apps sign in with an application password.
* Required for the roles you choose, with 0 to 30 days (7 by default) to set it up.
* Passkeys (WebAuthn) as the second step after the password, on HTTPS with the OpenSSL extension in PHP.
* See where your account is signed in, sign out everywhere else or sign out every user, and set how long an administrator's session lasts.

= Hardening =

One switch each, all off until you turn them on:

* The plugin and theme file editor and XML-RPC, without editing wp-config.php. ShieldWave warns you first if a plugin you use, such as Jetpack, needs XML-RPC.
* **PHP in uploads**: a block in the uploads .htaccess file on Apache and LiteSpeed, the rule to add on nginx and IIS, and Check now to test it.
* **Security headers**: nosniff, Referrer-Policy, Permissions-Policy, Cross-Origin-Opener-Policy, framing protection and, over HTTPS only, Strict-Transport-Security. A header already sent by your server, CDN or another plugin is left as it is.
* **Virtual patches.** With the live threat feed on, a small must-use file stops a request that exploits a known vulnerability of an installed plugin, for as long as the installed version is affected. Each patch only records matching requests until you let it block them.
* **Security updates** (WordPress 6.6 or newer), chosen per plugin: WordPress's own automatic updates install a release as soon as it fixes a known vulnerability of the installed version, and put the previous version back if the update fails.

A file with a finding of medium severity or higher can be moved into quarantine with one button, and Restore puts it back. The FAQ explains what is never moved.

= Activity log and privacy =

History is kept in its own database table. Each entry carries a keyed hash that includes the hash of the entry before it, so Check history integrity tells whether an entry was edited or deleted outside ShieldWave. Entries are kept for 7 to 730 days (90 by default) and can be exported as CSV. The plugin adds a suggested text to Settings > Privacy and takes part in the WordPress personal data export and erasure tools. The FAQ lists what it stores about logins.

= ShieldWave dashboard (optional) =

Press **Connect with shieldwave.io** under ShieldWave > Settings and the site shows up in your dashboard at [shieldwave.io](https://shieldwave.io/?utm_source=wordpress&utm_medium=plugin&utm_campaign=readme) with its score and open issues, next to ShieldWave's outside scan. A free account shows one site; Pro and Enterprise show every site. The dashboard cannot change anything on the site. See External services.

= What this plugin does not do =

It is not a full web application firewall and it does not clean malware out of files for you. You, your developer or your host make the change it explains. Ensomedia Security is built by ENSOMEDIA (Radosław Fedorczuk, Wrocław, Poland), which also runs shieldwave.io.

= Credits =

The admin screens use the Inter typeface by The Inter Project Authors, licensed under the SIL Open Font License 1.1 (`assets/fonts/inter-license.txt`). It is loaded from the plugin itself, only on the ShieldWave screens.

== Installation ==

1. Install the plugin from Plugins > Add Plugin, or upload the `ensomedia-security` folder to `/wp-content/plugins/`.
2. Activate it.
3. Open **ShieldWave** and press **Scan now**. The first scan records the baseline and usually takes a few minutes; later scans are faster.
4. Scheduled scans run daily at 03:00 (site time). Change that, the alerts and the options under **ShieldWave > Settings**.
5. Optional: turn on the known-vulnerability lookup under **ShieldWave > Settings > Live protection**.
6. Optional: to see the site in the ShieldWave dashboard, press **Connect with shieldwave.io** under **ShieldWave > Settings > ShieldWave account**.

== Frequently Asked Questions ==

= Do I need a ShieldWave account? =

No. All 23 checks, scheduled scans, email alerts and the vulnerability lookup work without one, with no limits.

= Can I hide ShieldWave from the toolbar? =

Yes. Under ShieldWave > Settings > Toolbar, turn off "Show ShieldWave in the toolbar". The choice is per administrator. Visitors never see it.

= How is this different from other security scanners? =

It trusts the files WordPress.org can vouch for, needs real evidence before it calls something malware, and emails only about problems that are new or got worse. The goal is that every alert you get is one you would want.

= What does "Ask your developer to check" mean? =

The scanner found something unusual that is often harmless: for example a new PHP file in a premium plugin without an update. It is listed so someone who knows the site can look at it, and it never sends an email on its own. Problems under "Fix now" are the ones with clear evidence.

= A finding is about a file I changed on purpose. =

Open the item and press "Ignore". It stays out of the score and the alerts, and an ignored file comes back by itself if it changes again. Ignored items stay under the "Ignored" link below the to-do list, where "Restore" brings one back.

= Will it slow my site down? =

No. Scans never run while a visitor's page loads: they work in the background in steps of a few seconds, and later scans skip files that were clean and did not change. On a busy shared host you can choose the low scan speed in Settings.

= What happens to the scheduled scan when the clocks change? =

It stays on the hour you chose, in the time zone set under Settings > General, and it follows a change of that time zone at once. On the one day a year the clocks skip the chosen hour, the scan runs right after the skipped hour. On the day an hour happens twice, it runs once, the first time the clock shows it.

= Which outside requests does it make? =

Only when scans run: to WordPress.org for checksums, theme packages, plugin information and the list of WordPress releases, and to your own site. With the vulnerability lookup, live threat feed or AI second opinion on: to shieldwave.io, once an hour from WP-Cron and after scans. With an account connected: to shieldwave.io, also once an hour. Each of those is off until you turn it on, and the External services section below lists exactly what is sent.

Requests to your own site: the XML-RPC, debug output and exposed files checks request xmlrpc.php, the debug.log URL, backup files found on disk and the uploads folder; the HTTPS check asks for the home page over http and https and the security headers check once over https. "Check now" for PHP in uploads writes two harmless test files there, requests them and deletes them. After a file is moved to quarantine or a security update, the plugin requests the home page and the login page to see that the site still answers. A running scan calls your own admin-ajax.php to continue in the background.

= Why does the plugin register a public AJAX action? =

The AJAX action `shieldwave_worker` lets a running scan continue in the background; it does nothing without the random token of the running scan. The plugin's REST routes answer administrators only.

= My headless front end reads authors from the REST API. =

With login protection on, the REST API user list answers only logged-in requests, so an anonymous request for an author gets an error. To keep the list public, add `add_filter( 'shieldwave_hide_user_list', '__return_false' );` to a small plugin or to your theme's functions.php. The same filter also brings back the author fields in embeds and the users sitemap.

= Does it work on multisite? =

Yes, network-wide. It is activated for the whole network (Network Activate): two-factor login and login protection need that, because one sign-in works on every site. Scanning, the schedule, alerts, the settings screens and the optional ShieldWave account all live on the main site, in the Network Admin, for network administrators only. Every other site of the network enforces the same choice made there: when login protection, two-factor or a hardening switch is on, it is on for that site too, with no separate settings screen to set up. A lockout after failed logins is shared by the whole network, not counted per site, so an address cannot dodge the limit by trying a different site.

= Does it work without WP-Cron? =

Yes. If WP-Cron is disabled or loopback requests are blocked, scans still run while the ShieldWave screen is open, and scheduled scans run whenever your server cron calls wp-cron.php.

= How is the score calculated? =

It starts at 100. For each check, the most serious open issue takes off points: critical 30, high 15, medium 8, low 3. The most serious issue on the site then sets the range, as in the outside scan on shieldwave.io: 0-39 with a critical issue, 40-59 with a high one, 60-100 with neither. Ignored issues, hints and checks that could not run take nothing off.

= Which languages does it speak? =

English, Arabic, Chinese (Simplified), Dutch, French, German, Indonesian, Italian, Japanese, Korean, Polish, Portuguese (Brazil), Russian, Spanish, Swedish, Turkish and Vietnamese. The screens, the login protection and the alert emails follow the language of your WordPress admin, and Arabic gets a right-to-left layout. Regional variants such as Spanish (Mexico), German (Switzerland) or French (Canada) use the main language. For these languages the plugin's own translation is used, and a translate.wordpress.org pack fills only what it lacks; other languages use the pack from translate.wordpress.org once one is complete.

= The old ShieldWave Security from shieldwave.io is installed too. =

Builds downloaded from shieldwave.io before the directory listing were called ShieldWave Security, numbered up to 3.6.6, and live in the folder shieldwave-security. They cannot update themselves to this plugin. Keep Ensomedia Security active, deactivate ShieldWave Security, then delete it on the Plugins screen: the settings, results and two-factor set-ups carry over, because Ensomedia Security keeps them while the old copy's clean-up runs. The Plugins screen shows a notice while an old copy is installed.

= What does quarantine do, and can I undo it? =

A finding of medium severity or higher about one file can move that file into quarantine. The file is stored encoded, so it cannot run, in a folder the web server refuses. wp-config.php, the main .htaccess, WordPress's own files and files the site loads on every request are never moved, and if the site stops working after a move, the file goes back. Nothing is moved unless you press the button.

Restore puts back the same bytes, with their date and permissions, where the file was. Quarantined files are kept in a folder inside wp-content that is removed, with the files the plugin put there, when the plugin is deleted, so restore what you want to keep before you delete the plugin.

= What does the plugin store about logins? =

When logins are paused after too many failed attempts, the plugin records the IP address and the typed user name (the name only when the account exists). After a successful login it sets a cookie whose name starts with shieldwave_kd_. It holds only a random identifier, lets that browser keep signing in while someone else guesses the password for the same account, expires after 90 days and stops working when the password changes.

= What happens to my data when I delete the plugin? =

The plugin removes its settings, results, tables, scheduled tasks, user settings, two-factor and passkey data, the quarantine folder and its block in the uploads .htaccess file. If the site is connected to shieldwave.io, it first asks shieldwave.io to remove the site and its results.

= What does connecting to shieldwave.io do? =

It shows the site in your shieldwave.io dashboard with its score and open problems, next to the outside scan of shieldwave.io, and confirms that the domain is yours, which the full outside scan needs. A free account shows one site. Everything in the plugin works the same without it.

= I think a finding is wrong. =

Open a topic in this plugin's support forum with the check name and what you see. False positives are treated as bugs.

= How do I report a security problem in the plugin? =

Follow https://shieldwave.io/legal/en/vulnerability-disclosure?utm_source=wordpress&utm_medium=plugin&utm_campaign=readme. Please do not post it in the public support forum.

== External services ==

No outside requests are made on activation or when admin pages load, only when a scan runs, when you use the account actions, and once an hour from WP-Cron for the live protection features you turned on and a connected account. Requests to your own site are listed in the FAQ. Requests to WordPress.org and your own site use the WordPress HTTP API's default user agent (WordPress version and site address), as WordPress's update checks do. Requests to ShieldWave send a neutral user agent (ShieldWave-Security and the plugin version) and never your site address in their headers. Each request comes from your server's IP address.

**WordPress.org core checksums** (api.wordpress.org/core/checksums/1.0/)
Used by the "WordPress core files" check. Sent: your WordPress version and package language. Cached for a day.

**WordPress.org release list** (api.wordpress.org/core/stable-check/1.0/)
Used by the same check to know which older releases to compare with. Sent: nothing about your site. Cached for a day.

**WordPress.org plugin checksums** (downloads.wordpress.org/plugin-checksums/)
Used by the "Plugin files" check. Sent: the folder name and version of each installed plugin. Cached for a week, or for a day when no list was available.

**WordPress.org theme packages** (downloads.wordpress.org/theme/)
Used by the "Theme files" check: the release package of each directory theme is downloaded to a temporary file, hashed and deleted. Sent: the theme's folder name and version. Cached for a week.

**WordPress.org plugin information** (api.wordpress.org/plugins/info/1.2/)
Used by the "Closed and abandoned plugins" check. Sent: the folder name of each installed plugin. Cached for a day.

**WordPress.org theme information** (api.wordpress.org/themes/info/1.2/)
Used by the "Theme files" check to learn which installed themes come from the directory. Sent: the folder name of each installed theme. Cached for a day.

All six are provided by WordPress.org: [privacy policy](https://wordpress.org/about/privacy/).

**ShieldWave vulnerability lookup** (shieldwave.io, endpoint /api/plugin/vulnerabilities), off until you turn it on
Used by the "Known vulnerabilities" check. Sent: your WordPress version and the folder name and version of each installed plugin and theme, premium ones included. A premium plugin's product name is compared on your site, never sent. No site address, no account, no personal data. The data comes from Wordfence Intelligence, and each result links to its record with its copyright notice; links go only to Wordfence, NVD, CVE.org, WPScan, Patchstack, shieldwave.io, WordPress.org and the GitHub advisory database. Runs with the scan and once an hour from WP-Cron; cached for a few hours.

**ShieldWave threat feed** (shieldwave.io, endpoint /api/plugin/intel), off until you turn it on
Used by the "Live threat feed" option and by virtual patches. The plugin downloads a signed file of extra malware rules, file signatures and virtual patch rules once an hour from WP-Cron and verifies its signature before use. Sent: nothing about your site; only the plugin version, which every request carries.

**ShieldWave AI second opinion** (shieldwave.io, endpoint /api/plugin/ai/review), off until you turn it on
For a file the local rules cannot judge. Sent: an excerpt of up to 6,000 characters around the suspicious code; the file's SHA-256 and MD5 fingerprints and size; its kind and where it sits (plugin or theme folder name, uploads and so on); the local verdict and the matched rule identifiers; the site language, the plugin version and a salted hash of the site. Removed before sending: your site address, email addresses, IPv4 and IPv6 addresses, the server paths of WordPress and of home folders (replaced by a neutral path), every DB_ constant and the arguments of database connections, values named like a password, key, salt, secret or token (from 6 characters) and long random tokens. Other web addresses stay. For PHP inside an image, the excerpt starts at the first PHP tag. Never read: wp-config*, .env*, .htpasswd, .user.ini, php.ini, auth.json, backup copies (.bak, .old, .orig, .save, .swp, names ending in ~), .sql dumps, logs and .ini files. If the redaction fails, nothing is sent. shieldwave.io passes the excerpt to an AI provider of shieldwave.io, described in the ShieldWave privacy policy; the verdict is kept by the file's fingerprint and shared across sites. Runs after a scan and once an hour from WP-Cron.

**Link to the outside check** (shieldwave.io)
The Overview links to the free outside check. Nothing is sent until you click; then your browser opens shieldwave.io/?check=yourdomain.com. Links to shieldwave.io carry utm_source=wordpress, utm_medium=plugin and utm_campaign=(the screen).

**ShieldWave account** (shieldwave.io), only after an administrator connects the site under ShieldWave > Settings

* Connecting opens shieldwave.io/connect in your browser with this site's address, the address to come back to and a random value. Back on the site, the plugin sends the single-use code, this site's id and address, and the plugin, WordPress and PHP versions to /api/plugin/connect/exchange and receives a key for this site. With a pasted API key, /api/license/verify receives the key and /api/plugin/connect the same site facts.
* While connected, the home page carries a meta tag named shieldwave-verify with a code of your account, and /api/plugin/verify-domain asks shieldwave.io to read it, after connecting and every few hours until the domain is confirmed.
* Hourly and after each scan: /api/plugin/heartbeat receives the site id, the site address and language, the versions of the plugin, WordPress and PHP, the scan status, the score and grade, the issue counts and the times of the last and next scan.
* After each scan (if sending is on): /api/plugin/sync receives the scan summary (counters, score, active theme, number of active plugins, site language and the language of the administrator who started the sync), the open and ignored issues (severity, confidence, title, message, fix, when first seen, the file, plugin or setting involved, rule identifiers, line, the plugin or theme and its version, the vulnerability identifier, score and record link, and the AI second opinion when there is one) and the installed plugins and themes with their names, versions and source.
* Never sent: posts, pages, comments, user names, email addresses or passwords. Issues about administrator accounts go with their title and fix only.
* For a site beyond the one site of a free account, ShieldWave stores no results and the plugin pauses these calls for 12 hours.
* "Disconnect" calls /api/plugin/disconnect, which removes this site and its results from shieldwave.io. Deleting the plugin from the Plugins screen makes the same call once, with a 5 second limit, before the key is removed.
* The key is kept in the database encrypted with a key derived from your wp-config.php salts, where the server has libsodium, and sent only in a request header.

The data above is processed by shieldwave.io, run by ENSOMEDIA in Poland, and for the AI second opinion by an AI provider of shieldwave.io. ShieldWave [terms of service](https://shieldwave.io/legal/en/terms-of-service?utm_source=wordpress&utm_medium=plugin&utm_campaign=readme) and [privacy policy](https://shieldwave.io/legal/en/privacy-policy?utm_source=wordpress&utm_medium=plugin&utm_campaign=readme).

== Screenshots ==

1. Overview: whether the site is safe, what to do next, and what protects it.
2. A problem opened: what was found, how to fix it, and a button to the right WordPress screen.
3. Signs of a break-in: what to do now, and the file, rule and code that matched.
4. A scan running in the background.
5. History: every scan and every change that matters, in plain sentences.
6. Settings: automatic scans, email alerts, extra checks and the optional ShieldWave account.
7. The optional ShieldWave account: connect the site to shieldwave.io with one click, or with an API key.

== Changelog ==

= 1.1.18 =
* No PHP warning on hosts whose open_basedir setting leaves out the folder above WordPress. The plugin checks that setting before it looks at a wp-config.php there, a debug log or any other file outside the allowed folders.
* Unused plugins are one finding with their number and names, and so are unused themes, so the Overview and shieldwave.io show the same count. A plugin or theme with a finding of its own keeps that finding too.
* The note on extra files of a plugin built outside WordPress.org says what ShieldWave did: it has no copy of the author's source for that version, so it did not compare those files.
* With WordPress in a subfolder of the site, a copy of wp-config.php in the folder above (the web root) is requested at the site address like a copy in the WordPress folder: high when it downloads, an info note when the server refuses it. Before, it was low without a request.
* After a plugin update the threat feed is downloaded again, and a stored copy that another plugin version checked is replaced even when the feed has not changed. Before, a copy an older version had stored without the vendor file lists stayed in use. "Copy for your developer" and the sync name the feed version, when it was issued and the plugin version that stored it.
* Security updates, off until you choose a plugin: when a release fixes a known vulnerability of the installed version, WordPress's own automatic updates install it (WordPress 6.6 or newer, which puts the previous version back if the update fails), the plugin then checks that the home page and the login page answer, and every step goes to the activity log.
* Virtual patches, off until you turn them on under Settings. With the live threat feed on, a small must-use file stops a request that exploits a known vulnerability of an installed plugin, for as long as the installed version is affected. Patch rules come only from the signed feed. Each patch only records matching requests until you let it block them, and a blocked request gets a plain 403. Turning the switch off removes the file.
* Arabic: severity names match shieldwave.io, and instructions address the reader without masculine imperatives.
* Polish: one-letter words stay with the next word, so none ends a line.
* A changed .htaccess, .user.ini or php.ini is described as a server configuration file: the steps for your developer say to undo the lines nobody added and keep the file, instead of treating it as a PHP file and suggesting to delete it.
* An update that adds files to a plugin or theme WordPress.org cannot verify, including one installed by uploading a ZIP, no longer makes every changed file "changed outside an update". Before, the files the update added were indexed first and made the update look older than the change.
* The readme fits the WordPress.org limits again: the description and External services are shorter, quarantine and login data moved to the FAQ, and the changes before 1.1.10 moved to changelog.txt.


= 1.1.17 =
* The score follows the same severity bands as shieldwave.io: an open critical finding keeps it at 39 or below, a high one between 40 and 59.
* Detects file-dropper backdoors: request input written into a PHP file, a written file's path chosen by the request, and decoder names assembled from pieces whose result is run.


= 1.1.16 =
* One score everywhere. The Overview, the weekly email, WP-CLI, the heartbeat and the sync all show the score stored with the last scan, and it is stored again when you ignore or restore an issue, when a live check runs and when an AI second opinion arrives. Before, the Overview and the sync could show two numbers for the same moment.
* A known vulnerability is rated by the access it needs. Without login it keeps the advisory's rating, and critical needs code execution, SQL injection, an authentication bypass, an arbitrary file upload or a CISA KEV entry. A subscriber, customer or contributor account makes it high at most, author, editor or shop manager medium at most, administrator low. An advisory without a rating is low, with a note. "Update to" names the version that fixes every listed advisory.
* One cause, one finding. An unused plugin WordPress.org closed for a security issue that also has a known advisory is one finding, which says it was closed, plus a note that it is unused. A file reported by more than one check (it differs from its release, it is new since the last scan, the malware check flags it) is counted once, at the most serious finding.
* WordPress PHP translation files (.l10n.php) next to a plugin's or theme's translations are no longer "a PHP file the release does not have". The malware check still reads them.
* A request from the server to its own https:// address that fails is a note that HTTPS could not be checked, not "login not served over HTTPS". A refused HTTPS port while http:// answers is still high.
* Scans started from WP-CLI or a system cron no longer judge the web server by the command line's PHP. The plugin remembers the web server's PHP version and display_errors from your admin pages. Until it has seen them, those two settings are not assessed. WP_DEBUG_DISPLAY set to null follows php.ini, as WordPress does.
* Notes instead of findings: an administrator created with WP-CLI, XML-RPC multicall when another login limiter is active or a firewall answers, PHP that stops getting security fixes within four months, and a leftover scheduled task with only a web address. On a network, a theme active on any site is in use, and a default theme a WordPress update adds is not "added outside the installer".
* A base64_decode counts only when the decoded text reaches code that runs or includes it, so the plugin no longer flags its own passkey code. Drafts, pending and private posts are read with the malware signatures only. Security keys follow WordPress's own rule (empty, the sample phrase or shared), not their length. A stale wp-content/debug.log is checked even when WP_DEBUG_LOG names another file.
* Private findings keep their identity when you replace the security keys in wp-config.php. Low findings no longer suggest looking for a break-in, and a few texts were reworded to say what was observed.


= 1.1.15 =
* What the scanner could not see is a note outside the score: a file too large to read in full, an XML-RPC check the site could not run against itself, a debug log it could not test or the server refuses, and a backup file the server refuses, did not answer about or was not asked about in this scan. A site whose only open items were such notes scored 86 (B). It now scores 100.
* A file counts as downloadable only when the server answers with the file's own first bytes. "PK" at the start of an answer, or any text that is not a web page, no longer proves it. A downloadable zip is listed: settings, keys or a database dump inside make it high, other files low. Installer and Adminer scripts are never requested, since a request runs them, so they are low until you remove them.
* A known vulnerability is rated by the access it needs. Without login it keeps the advisory's rating, and critical is kept only for code execution, SQL injection or an authentication bypass. With a subscriber-level account it is one step lower, with author, editor or administrator rights it is low, and an advisory without a rating is low, with a note.
* One cause, one finding. An update that a known vulnerability covers is listed with the vulnerability, an unused plugin or theme is listed once, and a WordPress minor release is low unless an advisory covers it. A plugin WordPress.org closed for a reason other than security is low. One closed for security is high only with a known advisory.
* Security keys WordPress ignores (the sample phrase, a value two keys share) are low. A short key it uses stays medium. On a network, where people who sign up get no role, registration is no longer judged by a site's default role.
* Scheduled tasks: code words match as whole words, so "retrieval" is not "eval". A task nothing runs that carries code is medium. One with only a web address is a low leftover of a removed plugin. "Treat the site as infected" appears only when the malware check flagged the code that runs a task with a known malware name.
* A new administrator that the activity log shows another administrator created is a note. A script from another site with no sign of malware is low, and "automated injection" needs such a sign. A changed core file is high (critical needs a malware match), a PHP file in uploads the analyzer found nothing in is low, and the files an update left behind are one low finding per plugin.
* A PHP version without security fixes is low, since hosts often backport fixes. Pingbacks are not reported when pings are closed on the whole site. The public user list counts only when a visitor really gets it (a block by the server or a firewall counts as closed), and it is called a list of username slugs.
* One grade ladder, the one shieldwave.io uses: A from 90, B from 80, C from 60, D from 40. The heartbeat sends the stored score of the last scan, the number the sync sends, and the Overview headline reads the score the Overview shows.
* Texts that sounded alarming without evidence were rewritten, and texts the plugin no longer uses were removed from the translations.

= 1.1.14 =
* A plugin its author ships outside WordPress.org, such as Advanced Custom Fields from WP Engine with its own Update URI line and updater files, is compared without that one header line, and its extra files with the author's published source of the same version, which the threat feed now carries. Until now the main file was "changed" and the updater files were "unknown PHP" (high). A file the published source does not have is still reported high. Without a published source the extra files are one note that they could not be verified, and the malware check reads them more strictly.
* Checksum lists from WordPress.org that also list the plugin's files under trunk/ (YITH WooCommerce Product Add-ons 4.34.1 lists 441 files twice) no longer make every clean install report hundreds of missing files. Missing files of one plugin or theme are one finding with the count and a few paths. A missing file runs no code.
* Code snippets of WPCode, Simple Custom CSS and JS and Woody snippets are read with the malware signatures, active or not, and an active PHP snippet is no longer "PHP code stored in content". A PHP tag in a normal post is still reported.
* A known vulnerability in a plugin or theme nothing uses is one step lower, and its fix is to delete it instead of deactivating it.
* Severities follow what someone could do on the site now. The file editor, an easy administrator login name, updates without a known vulnerability, a changed .htaccess without PHP rules, a phpinfo() file the site does not serve, an unknown PHP file in the site root the malware signatures find nothing in, and a backup copy that could not be requested are low. A WordPress minor release and errors shown on the page are medium. A copy of wp-config.php that downloads as text is high.
* XML-RPC: WordPress stops checking passwords in a request after the first wrong one, and login protection counts XML-RPC logins, so with login protection on the multicall finding is a note.
* The Overview says "signs of a break-in" only for a malware match, a web shell, administrator powers outside the role or a malicious code snippet. A file that differs from its release is listed under "Fix now" as a weakness. Finding texts no longer guess at an attack where only a difference was found.
* The sync to shieldwave.io sends every open issue (up to 500, it was cut at 200) with its fingerprint and first-seen time, and says whether the list is complete, so "new since the last scan" counts only new findings.

= 1.1.13 =
* No more false alarms right after an update. The file changes check finishes again (from 1.1.10 to 1.1.12 it stopped with an error, so its findings never closed), a file that matches its release keeps its official mark through the walk that follows an update, and a plugin or theme whose every code file matches WordPress.org is never "changed without an update", even when the update left no record, as automatic updates of this plugin do.
* The malware check no longer calls a plugin's own admin code malicious when request input reaches eval or include behind a nonce and capability check that ends the request, and an include path chosen from a fixed table by a request key is not request input. Dummy checks and checks on one side of an "or" are still caught.
* A file of a WordPress.org plugin is judged more strictly only when it stands out: it differs from the release, is not part of it, or the rest of the plugin matched while it carries no mark. Until now, when the marks were gone after an update or WordPress.org had no checksums for the version, every file of the plugin was read that way and one weak sign was enough to report it (WP Mail SMTP's e-mail summary template).
* Six rules were narrowed after a sweep of 147,858 files from the 200 most popular WordPress.org plugins: a login form that checks the password it was sent, the WordPress importer and the copies themes bundle, assert used as a type check, a stylesheet printed inside a style tag, a variable named $include, and a shipped build script that writes PHP from its own text are no longer reported. The backdoors they resembled still are, each with a sample in the release tests.
* Drafts of code snippets (Simple Custom CSS and JS, WPCode) are not injected content; published snippets are still checked.
* A large script that is not PHP in a node_modules, vendor or bower_components folder of a plugin or theme gets no "too large to analyze" note of its own; both ends are still analyzed.
* The Overview headline says the site needs some tidying, in amber, when the score is under 60 or five or more things are open with nothing urgent. It no longer says "Your site looks good" over a failing score.
* The live threat feed card says "nothing new beyond the built-in rules" instead of "0 rules, 0 known-bad files".
* A theme that loads its front-end CSS in wp-admin no longer draws a line across the ShieldWave header.
* A security review of the whole plugin (admin screens, REST and the scan worker, the connection to shieldwave.io, login and two-factor, passkeys, sessions, quarantine, every check and the rules engine). What it found is fixed here. For a site updated from 1.1.x with a trusted proxy, the visitor address header is no longer picked up from a header a visitor could have added; only X-Forwarded-For is adopted on its own. A live login lockout is no longer lifted by a sign-in finished later on the two-factor screen. Turning the scan schedule or the alerts off, raising the alert threshold, adding an alert recipient, excluding files and starting the file history over now ask for your password, as turning a protection off does. Serialized objects inside the posts, widgets and builder data the content check reads are never created. A post of many megabytes no longer holds the scan. A server rule that runs images as PHP through mod_rewrite, a redirect to another site inside a caching plugin's block, a page or redirect saved as a "placeholder" in uploads, and a firewall bootstrap that only names the firewall's folder in a comment are reported. File names are shown and quarantined exactly as stored, including a leading space or a marker inside the name. Paths longer than 1,024 characters are stored whole. Findings whose severity depends on the state of the site (an exposed backup, an available update, a known-bad scheduled task) come back after an ignore when that state gets worse. The history log refuses to seal over a change made outside the plugin, notices the deletion of its oldest entries and uses a lock name of its own per install; the personal data eraser also removes the address. Freshly issued recovery codes are stored encrypted. The excerpt sent to the AI second opinion also loses bearer tokens, positional random tokens and password values. Answers from shieldwave.io and WordPress.org have size limits, feed rules are tested for cost before use, and the detection of encoded files keeps to the time budget.
* The readme says more precisely what the sync, the AI second opinion and the checks send and to whom, names the theme information service, and says which translation is used for the shipped languages.
* Before release, the plugin went through a scan of a test site with the 200 most popular WordPress.org plugins and the plugins of five real sites, an update of a WordPress.org plugin, a premium plugin, a theme and WordPress core by FTP and through WordPress, and every check had to finish.

= 1.1.12 =
* Quarantine with restore for findings of the malware, PHP in uploads, core, file changes, plugin and theme checks. The file is stored encoded so it cannot run, and Restore puts back the same bytes.
* Block PHP execution in the uploads folder and send security headers, each with its own switch, off by default. On nginx and IIS, Settings shows the rule to add.
* Two-factor login can be required for chosen roles, with 0 to 30 days to set it up. Passkeys (WebAuthn) work as the second step. Sessions can be listed and ended everywhere, and an administrator's session length can be set.
* The suggested privacy text now describes what two-factor login and passkeys store for an account.
* Deleting the plugin also removes the quarantine folder, the passkey data and the uploads .htaccess block.
* Setting up an authenticator app asks for your password, and the owner gets an email for every new app. An account whose time to set up two-factor is over gets a code by email before the set-up screen.
* Changes that lower protection (higher login limits, a trusted proxy, the file editor back on, a longer session) ask for your password, wherever they are made.
* On a multisite network the plugin is network activated, so two-factor and login protection cover every site.
* Quarantine refuses files the site loads on every request, checks the site still works after a move, and puts the file back if it does not.

= 1.1.10 =
* Login protection: a limit per user name, a limit on password reset requests, trusted proxies as a list of addresses or CIDR ranges with a choice of header, and an email when an administrator's user name is locked.
* The activity log has its own table, its ordinary entries form a hash chain whose newest entry is recorded apart, so edited, deleted and cut-off entries are found, the retention is set in days and the log can be exported as CSV.
* Privacy: a suggested text for Settings > Privacy, and the WordPress personal data export and erasure tools cover the failed login records.
* The AI second opinion redacts more before anything is sent (IP addresses, paths, all DB_ constants, database connection arguments, secrets from 6 characters), never reads configuration, credential, backup, dump, log and ini files, and starts the excerpt of PHP in an image at the PHP tag.
* The banner that offers live protection now says that the AI second opinion sends redacted excerpts to shieldwave.io and its AI provider.
* The API key is stored encrypted when the server allows it.
* Deleting the plugin asks shieldwave.io to remove a connected site, and removes the tables, scheduled task and user settings this version adds.
* A record link in a vulnerability finding is shown only for known vulnerability databases.
* The readme describes what is sent and how often as the code does it: the threat feed and the lookups run every hour from WP-Cron, live protection has three features.
* Malware detection follows request input more precisely and no longer flags legitimate code. On a test site with 35 popular plugins, 5 themes and WordPress core (52,800 files) it flags nothing, and no file takes more than a fraction of a second. Large PHP files are analysed within a time and memory budget.
* File checks: a file that differs from its release only in line endings matches it, a change made by a recorded update is explained, and a real change stays reported until it is reviewed. Links to other sites and odd file names are one finding per place.
* Known vulnerabilities: when a fixed version exists but WordPress does not offer the update (a plugin closed on WordPress.org, a premium plugin, or an update source that has no newer version), the finding says so and gives the steps for that case.
* HTTPS: when the site address starts with http but every http address redirects to https, this is information, not a problem.
* Login and two-factor: turning two-factor off for the whole site needs the password or a code, a changed or reset password forgets the remembered browsers, wp shieldwave unlock <user> lifts a two-factor lock, and ?author= requests in any form no longer reveal a login name.
* Alerts, notices and log entries use calmer, more precise wording.
* Findings that name user accounts reach a connected shieldwave.io account without the names.
* Every text was reviewed again for natural wording in all 16 languages, and 23 unclear English texts were rewritten.

Changes before 1.1.10 are in changelog.txt in the plugin folder.

== Upgrade Notice ==

= 1.1.18 =
Virtual patches and security updates (both off by default), no open_basedir warning in wp-admin, unused plugins and themes counted as one finding each, and no false file changes after an update by ZIP.


= 1.1.17 =
The score follows the same severity bands as shieldwave.io, and file-dropper backdoors are detected.


= 1.1.16 =
One score everywhere, vulnerabilities rated by the access they need, one finding per cause, and no judgements of the web server from WP-CLI or from a failed request of the server to itself.


= 1.1.15 =
Fewer false findings and one score: what could not be verified is a note, downloads are proven by their bytes, vulnerabilities are rated by the access they need, and one cause gives one finding.

= 1.1.14 =
Fewer false findings: vendor builds such as ACF from WP Engine are verified, copied SVN folders and missing files no longer flood the list, active code snippets are not PHP in content, and severities follow what someone could do on the site now.

= 1.1.13 =
No more false alarms after an update: file changes close again, fresh files keep their official mark, guarded admin code is not malware, draft snippets and bundled libraries are quiet. The headline says when the site needs tidying.

= 1.1.12 =
Adds quarantine with restore, PHP blocking in uploads, security headers, required two-factor for roles, passkeys and sessions. Each is off until you turn it on or press its button. Hardens two-factor set-up and re-authentication. On multisite, network activate the plugin.

= 1.1.10 =
Closes every finding of the security audit of 1.1.9: detection gaps, login protection, two-factor, privacy and robustness. Adds an activity log with hashes and privacy tools, and sends less to the AI second opinion.

= 1.1.9 =
Delete unused plugins from their finding, and eight fewer false alarms (old WordPress files, premium plugins, debug.log, HTTPS, TranslatePress).

= 1.1.8 =
Fixes a scan error on hosting whose PHP has no tokenizer extension.

= 1.1.7 =
Findings say which plugin, theme or folder a file is in and what to do there. An old ShieldWave copy is no longer reported as malware.

= 1.1.6 =
Fixes the screens on a multisite network and stops two old WordPress records from being reported as vulnerabilities.

= 1.1.3 =
Plain images in uploads are no longer reported as PHP files. Run a scan after updating to clear the old findings.

= 1.1.2 =
The screens keep the size of the WordPress admin on large monitors instead of being enlarged.

= 1.1.1 =
The automatic scan keeps the hour you chose when the clocks change or you switch the time zone. Update before the clocks change.

= 1.1.0 =
Connect with shieldwave.io in one click, one free site in the dashboard, and a safe way to remove the old ShieldWave Security.

= 1.0.3 =
A clearer Overview, the look of shieldwave.io, and screens that fit large monitors and phones.

= 1.0.2 =
Alert emails now display correctly in Outlook on Windows, in light and dark mode.

= 1.0.1 =
The plugin now speaks 16 languages besides English, including Arabic with a right-to-left layout.

= 1.0.0 =
First release in the WordPress.org directory.
