=== IronGuardWP ===
Contributors: ironguardwp
Tags: security, malware, firewall, malware scanner, login security
Requires at least: 6.0
Tested up to: 6.8
Requires PHP: 7.4
Stable tag: 1.2.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

All-in-one WordPress security: malware scanning, firewall, and login protection. Pure PHP, so it runs on any host.

== Description ==

IronGuardWP protects WordPress sites from malware, brute-force attacks, and known vulnerabilities. It scans your files against a curated malware definition set, blocks malicious traffic before it reaches WordPress, and tells you in plain language what happened and what to do about it.

**It runs anywhere.** The scanning engine is pure PHP. No ClamAV, no YARA, no SSH, no command line, no server access. If WordPress runs, IronGuardWP runs.

**Curated definitions, not a firehose.** Every signature is normalised into our own format, de-duplicated, and false-positive tested against a clean WordPress corpus before it reaches your site. Fewer false alarms on legitimate plugin code.

**Plain-language findings.** Every result says what happened, why it matters, and the recommended next step. No raw rule IDs as the headline, no unexplained jargon.

= What each plan includes =

Free, for one personal site:

* **Signature malware scan** — checks your files against known malware signatures and flags anything that matches.
* **Core integrity check** — compares WordPress core files against the official versions to spot tampering.
* **Login limiting** — locks out an attacker after repeated failed logins so brute-force attacks fail.
* **Security score** — a single score with a prioritised list of what to fix first.
* **Basic hardening** — one-click fixes for the common WordPress mistakes attackers rely on.

Plus adds:

* **Full / deep scan** — scans themes, plugins, and uploads, not just core files.
* **Real-time monitoring** — watches for file changes as they happen instead of waiting for the next scan.
* **Web application firewall** — blocks malicious requests before WordPress ever runs them.
* **IP allow / block and auto-blocking** — manual lists plus automatic blocking of attacking addresses.
* **Two-factor authentication** — a second step at login so a stolen password is not enough.
* **Security headers** — adds the HTTP headers that defend against clickjacking and content injection.

Pro adds:

* **Virtual patching** — shields a known plugin vulnerability at the firewall until the author ships a fix.
* **Shared threat feed** — addresses attacking one IronGuardWP site are blocked across the whole network.
* **Geo-blocking** — blocks traffic from countries you do not do business with.
* **Compromised password check** — warns when a user's password appears in a known breach.
* **Vulnerability alerts** — tells you when a plugin or theme you run has a newly disclosed CVE.
* **Auto-remediation** — cleans or repairs infected files automatically, with a restore point first.
* **Multi-site management** — all your sites and their security status in one dashboard.
* **Scheduled reports** — emailed security summaries, useful for client reporting.

Definition updates reach Free weekly, Plus daily, and Pro in real time.

= Design principles =

**It fails open.** If something in IronGuardWP goes wrong, it stops protecting rather than stopping your site. A failing component switches itself off and tells you, instead of throwing an error on every page. There is a documented emergency-disable procedure below, and lockouts always lift by themselves.

**Every destructive action is reversible.** Quarantine, repair, and delete all save a restorable copy first. If the copy cannot be saved, the action does not happen. Everything is restorable from the Quarantine screen with one click.

**It is built for shared hosting.** Scans run in small batches, pause when the server is busy, respect your `memory_limit` and `max_execution_time`, skip oversized files, and resume across requests. A scan that is interrupted picks up where it left off.

= Licensing =

One licence covers one site, meaning one domain. `www.example.com` and `example.com` are the same site. Separate domains need separate licences. Each licence also allows a small number of development environments, which the licence server identifies from your domain — there is no setting for it, and the plugin never claims to be a staging site on its own.

Billing, renewals, and cancellation happen through Whop. If you cancel, your licence runs to the end of the paid term and then the plugin drops back to Free. Nothing breaks and your site is never locked.

== Installation ==

1. Upload the `ironguardwp` folder to `/wp-content/plugins/`, or install the ZIP through Plugins, Add New, Upload Plugin.
2. Activate IronGuardWP through the Plugins screen.
3. Follow the **Set up IronGuardWP** prompt. Enter an email address and IronGuardWP claims a free licence for this one site — no payment details, now or later. If you already have a licence key, paste it on the same screen instead.
4. Go to **IronGuardWP, Scan** and run your first scan.
5. Work through the "What to fix first" list on the dashboard.

No Composer, no build step, and no command line are required. The plugin is a plain drop-in.

**Optional, recommended:** on the Firewall screen, turn on *early loading*. This installs a small must-use plugin so the firewall runs before themes and other plugins load, which catches attacks aimed at other plugins before that plugin's own code runs. It can be turned off again from the same screen, and it is removed automatically when you deactivate IronGuardWP.

== Frequently Asked Questions ==

= Will this slow my site down? =

Scans are incremental and throttled, and pause under load. The firewall adds a small amount of work per request. IronGuardWP is built for shared hosting — staying inside your host's CPU and memory limits is a design requirement, not an afterthought.

= Do I need server access or special software? =

No. The scanning engine is pure PHP. No ClamAV, no YARA, no SSH, no command line.

= What counts as one site? =

One domain. `www.example.com` and `example.com` count as the same site. Separate domains need separate licences.

= How do I get a free licence? =

Activate the plugin and follow the setup prompt: enter an email address and IronGuardWP asks our licence server for a free licence for this domain. No card is requested.

Sometimes a one-click checkout link comes back as well. If it arrives **alongside** your licence, your site is already protected and finishing the account is optional — the prompt is dismissible and nothing waits on it. If it arrives **instead of** a licence, open it, confirm, and come back to the setup screen to finish. Either way the total is zero and no card is needed.

A domain can be given a free licence **once, ever**. If this domain has already had one, IronGuardWP says so plainly rather than pretending something went wrong. Enter the existing key, or pick a paid plan.

If our licence server cannot be reached, nothing breaks: the plugin stays installed and usable, and you can try again whenever you like.

= What happens if I cancel? =

Your licence runs to the end of the paid term, then the plugin drops back to Free. Nothing breaks and your site is never locked.

= Can a plugin like this lock me out of my own site? =

IronGuardWP is designed to fail open. Login lockouts are always time-limited and lift by themselves; an allow-listed address is never locked out; two-factor is only switched on for an account after that account has proved a working code. And there is an emergency-disable procedure below that works entirely over FTP, without the dashboard.

= You found malware — what now? =

Every finding tells you what was found, why it matters, and the recommended action. Quarantining a file saves a restorable copy first, so if a finding turns out to be a false positive you can put the file back with one click from the Quarantine screen.

= What happens if the licence server is unreachable? =

Nothing changes. Your features stay exactly as they are, and the plugin retries with a backoff. A network problem, a server outage, or a bad response never reduces what you have paid for. If our server stays unreachable for more than two weeks, the plugin verifies your licence directly with Whop instead — and even then it can only confirm what you already had, never less.

= What does the plugin send back to IronGuardWP? =

A daily check-in with counts, rule names, and the addresses that attacked your site. It never sends your content, file contents, usernames, email addresses, or your visitors' addresses. The Settings screen shows you the exact data that would be sent, and you can switch check-ins off entirely.

= Does country blocking work on my host? =

Only if your site is behind a CDN or proxy that tells us the visitor's country, such as Cloudflare. IronGuardWP does not bundle an IP-to-country database — that would mean shipping a large binary file or looking up every visitor with a third party. The Settings screen tells you plainly whether it can work on your server.

== Emergency disable ==

**If IronGuardWP is causing a problem, use one of these. Both work without dashboard access.**

= 1. The constant (recommended) =

Edit `wp-config.php` and add this line **above** the `/* That's all, stop editing! */` comment:

`define( 'IRONGUARDWP_DISABLE', true );`

The plugin stops loading immediately. WordPress carries on completely normally, the plugin stays "active" on the Plugins screen but does nothing, and no settings or data are lost. Remove the line to switch it back on.

To disable **only the firewall** while leaving scanning and the dashboard working:

`define( 'IRONGUARDWP_DISABLE_FIREWALL', true );`

To disable **only two-factor authentication** — for example if someone has lost their authenticator app and their recovery codes:

`define( 'IRONGUARDWP_DISABLE_2FA', true );`

= 2. Rename the folder =

Over FTP, SFTP, or your host's file manager, rename:

`wp-content/plugins/ironguardwp`

to anything else, for example `ironguardwp-off`.

WordPress deactivates a plugin whose main file has disappeared, so the plugin is switched off on the next page load. The must-use firewall loader, if you installed it, detects the missing folder and turns itself into a no-op rather than causing an error.

To restore, rename the folder back and reactivate the plugin from the Plugins screen.

= 3. If you installed early loading =

Early loading adds one file:

`wp-content/mu-plugins/ironguardwp-firewall.php`

Deleting that file is safe and removes early loading. Nothing else depends on it. It is also removed automatically when you deactivate IronGuardWP.

= Locked out of wp-admin? =

Login lockouts are stored as WordPress transients and always expire on their own — the default is 20 minutes. Waiting is enough. If you cannot wait, use the constant in step 1: it clears the lockout check along with everything else.

== Screenshots ==

1. The dashboard: security score, outstanding findings, and a prioritised list of what to fix first.
2. Findings, each in plain language: what happened, why it matters, and what to do.
3. The scan screen, showing incremental progress and the installed definition set.
4. The firewall log and IP allow / block lists.
5. Quarantine, where every action taken on a file can be undone.

== Upgrade Notice ==

= 1.0.0 =
First release.

== Changelog ==

= 1.2.0 =
* Changed: the admin screens are now light only. On a computer set to dark mode the plugin's panels turned near-black while WordPress stayed light, which left settings labels and every other row of the activity tables unreadable. Colours are now measured against WordPress's own surfaces.
* Fixed: "Check for new definitions" reported a failure when your plan simply had nothing published yet. It now says so plainly and checks again on the normal schedule.
* Fixed: the message "your existing definitions are still in use" appeared on sites that had none installed.
* Fixed: scan progress showed a counter that cannot move during the core file check, which made a working scan look stuck. Each stage now shows the count that is actually moving, and the progress bar says it does not know rather than claiming 1%.
* Fixed: the setup screen returned a permissions error after you finished setting up.
* Fixed: screens no longer say files are checked against malware signatures when no signatures are installed yet.

= 1.1.0 =
* New: security dashboard with metrics and charts, drawn as inline SVG with no external requests. Every chart carries a text table with the same numbers.
* New: WordPress dashboard card showing protection status, security score, findings and blocked attacks at a glance.
* New: first-run setup — claim a free licence with just an email address.
* New: firewall rate limiting and progressive login lockout (off by default), CIDR allow/block lists with expiry and import/export, and virtual patching.
* New: richer block logging, so you can see why a request was refused.
* Fixed: every form in the plugin returned a truncated page and lost its confirmation message. Saving settings, starting a scan, checking definitions and recalculating the score are all repaired.
* Fixed: saving settings on the free plan could silently switch off deep scanning, the firewall, security headers and two-factor, so a site that upgraded later found them off.
* Fixed: a never-scanned site reported a security score it had not earned. It now says "Not yet scored" until the checks have actually run.
* Fixed: charts showed a zero for days before the plugin was installed. Days with no reading are now shown as gaps, not as zeroes.
* Fixed: a constant name clash could print a PHP warning above the page and put a site into quirks mode.
* Changed: severity colours in charts are now distinguishable, with a labelled legend that works without colour.

= 1.0.0 =
* First release.
* Pure-PHP signature scanning engine with regex, hash, and substring signature types, run under strict PCRE backtracking limits so a bad pattern can never take a site down.
* Hash signatures support sha256 and md5, and refuse degenerate digests so one bad definition cannot flag every empty file on a site.
* Incremental definition updates, applied only on top of the exact version they were built against.
* WordPress core file integrity checking against the official checksums API.
* Incremental, throttled, resumable scanning that respects shared-host memory and execution limits.
* Web application firewall with an optional must-use early-load path, plus virtual patching on Pro.
* Login limiting, two-factor authentication, IP allow / block lists with automatic blocking, geo-blocking, and the compromised-password check.
* Security score with one-click hardening fixes.
* Reversible quarantine, repair, and delete, each with a restore point.
* Licence client with HMAC-verified, replay-protected entitlements that never downgrade a site because of a network failure.
