=== Slotbaas - Security, Scanner & Firewall ===
Contributors: hvmediabv
Tags: security, malware scanner, firewall, auto updates, login security
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.6.9
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

De lichte totaaloplossing voor een WordPress-website die goed op slot zit: firewall, security scanner, brute-forcebescherming en automatische updates in één plugin.

== Description ==

Slotbaas is de lichte totaaloplossing voor een WordPress-website die goed op slot zit. Eén overzichtelijke plugin combineert een firewall, brute-force- en wachtwoordresetbescherming, malware- en integriteitsscans, beheerderscontrole en automatische updates.

Geen onnodige verzameling zware beveiligingsplugins, maar één complete beveiligingslaag die aanvallen helpt blokkeren, verdachte wijzigingen zichtbaar maakt en beheerders zelf de controle laat houden. Slotbaas is ontworpen om terughoudend met servercapaciteit om te gaan: scans draaien in kleine, gespreide stappen en de firewall blijft fail-open als een interne controle onverwacht niet kan worden uitgevoerd.

Slotbaas is ontwikkeld naar aanleiding van een echte website-inbraak die drie maanden onopgemerkt bleef. Elke detectieregel is gebaseerd op iets wat daadwerkelijk op een besmette website is aangetroffen.

De plugin beschermt de website op vier belangrijke onderdelen.

= 1. Nieuwe beheerders moeten worden goedgekeurd =

Een ongewenst beheerdersaccount kan ervoor zorgen dat een aanvaller ook na een schoonmaak toegang houdt. Zodra een account beheerdersrechten krijgt, parkeert Slotbaas het account daarom eerst:

* De oorspronkelijke rollen en rechten worden veilig opgeslagen.
* Het account krijgt tijdelijk een rol zonder bevoegdheden.
* Inloggen wordt geblokkeerd en bestaande sessies worden beëindigd.
* De beheerder ontvangt een e-mail met gebruikersnaam, e-mailadres, IP-adres, tijdstip en de manier waarop het account is ontstaan.

De e-mail bevat afzonderlijke links om het account goed te keuren of te weigeren. Deze beveiligde links verlopen na 48 uur, werken één keer en vragen altijd om een extra bevestiging. Een geweigerd account wordt geblokkeerd maar niet automatisch verwijderd, zodat het eerst onderzocht kan worden.

= 2. Beveiligingsscanner =

De scanner controleert onder meer op:

1. **PHP-bestanden in `wp-content/uploads/`**, waar normaal alleen media en documenten thuishoren.
2. **Onbekende bestanden in `wp-content/mu-plugins/`**, die automatisch bij iedere paginaweergave worden geladen.
3. **Verdachte pluginmappen met willekeurige achtervoegsels**, zoals die bij verborgen nepplugins worden gebruikt.
4. **Verdachte code in drop-ins** zoals `object-cache.php`, `advanced-cache.php` en `db.php`. De inhoud wordt beoordeeld, niet alleen de bestandsnaam, zodat legitieme cachebestanden worden herkend.
5. **Aangepaste WordPress-kernbestanden**, vergeleken met de officiële checksums van WordPress.org.
6. **Afwijkende bestandsdatums** die kunnen wijzen op kunstmatig teruggezette wijzigingsdatums, met correcties voor verhuizingen, back-ups en normale updates.
7. **Meerdere pluginheaders in één map**, een techniek waarmee schadelijke code zich als meerdere normale plugins kan presenteren.
8. **Oude WordPress-installaties in submappen**, die bereikbaar kunnen blijven zonder nog beveiligingsupdates te ontvangen.

De scanner werkt in kleine batches met een beperkt tijdsbudget. Websites krijgen verschillende, vaste scanmomenten en een drukke server stelt scanwerk automatisch uit. Na de eerste scan wordt alleen opnieuw onderzocht wat werkelijk is veranderd. Slotbaas maakt daarbij geen terugkerende HTTP-verzoeken naar de eigen website.

Iedere bevinding toont het pad, de bestandsgrootte, relevante datums, een uitleg in gewone taal, de eerste regels van het bestand en de functie van het bestand binnen WordPress, een plugin of het actieve thema.

= 3. Firewall en brute-forcebescherming =

De firewall controleert verzoeken voordat WordPress deze volledig verwerkt. De bescherming omvat onder andere verdachte REST-verzoeken, gebruikersopsomming, SQL-injectiepatronen, directe toegang tot PHP-bestanden in uploads, XML-RPC, auteursopsomming en misbruik van `wp-login.php`, `admin-ajax.php` en wachtwoordherstel.

IP-adressen kunnen afzonderlijk of als CIDR-bereik worden toegestaan of geblokkeerd. De optionele strenge modus kan een adres na herhaald misbruik permanent blokkeren. Vertrouwde adressen worden nooit automatisch op de blokkadelijst gezet.

**De firewall werkt fail-open.** Als een interne controle onverwacht niet kan worden uitgevoerd, blijft de website bereikbaar. Bij een normale paginaweergave gebruikt de firewall één reeds automatisch geladen instelling en enkele snelle tekenreekscontroles, zonder extra databasequery, bestandsbewerking of extern HTTP-verzoek.

Proxyheaders worden alleen vertrouwd wanneer de beheerder dit expliciet inschakelt en de eigen proxyadressen opgeeft. Zo kan een aanvaller de IP-controle niet omzeilen met een vervalste `X-Forwarded-For`-header.

= 4. Automatische updates =

Verouderde plugins en thema's vormen een belangrijk beveiligingsrisico. Slotbaas schakelt de gewone automatische updates van WordPress in en houdt nieuwe plugins en thema's eveneens bij. De zichtbare schakelaars in wp-admin blijven daarbij leidend.

Zet een beheerder een automatische update handmatig uit, dan voegt Slotbaas dit onderdeel toe aan de uitzonderingslijst. Kernupdates zijn standaard beperkt tot beveiligings- en onderhoudsversies; grote versie-upgrades zijn een expliciete keuze.

= Veilige, rapporterende scanner =

Slotbaas wijzigt, verplaatst, herstelt of verwijdert geen aangetroffen bestanden. De scanner geeft het pad, de reden en voldoende context om een bevinding te beoordelen. Herstel WordPress-kernbestanden via **Dashboard → Updates → Opnieuw installeren** en behandel bevestigde malware via het bestandsbeheer van uw hosting of met hulp van een deskundige.

= Taal =

De beheeromgeving is volledig vertaalbaar via translate.wordpress.org. Nederlands is momenteel de brontaal.

= Externe diensten =

Slotbaas benadert WordPress.org uitsluitend voor de scanner:

* `https://api.wordpress.org/core/checksums/1.0/` levert de officiële checksums voor de controle van kernbestanden. Hierbij worden de geïnstalleerde WordPress-versie en taalcode verstuurd. Deze controle kan in de instellingen worden uitgeschakeld.
Deze diensten worden beheerd door het WordPress-project. De voorwaarden staan op `https://wordpress.org/about/terms-of-service/` en het privacybeleid op `https://wordpress.org/about/privacy/`.

Slotbaas stuurt geen gegevens over de website, gebruikers of bezoekers naar HV Media. De plugin bevat geen licentiecontrole, registratie, telemetrie of analytics.

== Installation ==

1. Upload the `slotbaas` folder to `/wp-content/plugins/`, or install the ZIP through Plugins → Add New → Upload Plugin.
2. Activate the plugin through the Plugins menu.
3. Go to **Slotbaas → Settings** and fill in the email address that should receive security notifications. This is required: until it is set, notifications fall back to the site's admin email address and a reminder is shown.
4. Add your own office IP to **Firewall → Always allow** so you cannot lock yourself out.
5. Start a first scan at **Slotbaas → File scan**. It builds the baseline, which makes later scans much faster.

On activation the plugin creates its own tables, registers a capability-free role for parked accounts, records every administrator that already exists (they are never locked out), and schedules its cron events.

= Emergency switches =

If anything goes wrong, these constants in `wp-config.php` work even when you cannot log in:

`define( 'HVSEC_DISABLE_ALL', true );`
`define( 'HVSEC_DISABLE_FIREWALL', true );`
`define( 'HVSEC_DISABLE_USER_GUARD', true );`
`define( 'HVSEC_DISABLE_SCANNER', true );`
`define( 'HVSEC_DISABLE_AUTO_UPDATES', true );`
`define( 'HVSEC_ALLOW_IP', '203.0.113.4' );`
`define( 'HVSEC_NOTIFY_EMAIL', 'you@example.com' );`

An active emergency switch is shown as a warning on the overview screen, so a temporary measure does not silently stay in place for months.

== Frequently Asked Questions ==

= The scanner reported a core file, but the file looks completely normal. =

Use **Re-evaluate findings** on the scan page. Version 1.1.0 compares core files against both your site locale and the en_US package; a site installed in English and later switched to another language used to trigger a false alarm on `wp-includes/version.php`.

= I get lots of "back-dated modification time" findings. =

That was a weakness in version 1.0.0. On a site that has been migrated or restored, every file gets a fresh inode date while keeping its original modification date. Version 1.1.0 only reports a file whose date stands out from the other files in the same directory. Press **Re-evaluate findings** to clear the old ones.

= The scanner reports dozens of "back-dated modification time" findings about an archive folder. =

Update to 1.2.0 and press **Re-evaluate findings**. Those findings are withdrawn automatically. What remains is one finding about the archive folder itself, which is the thing that actually needs your attention: an old WordPress installation in the web root is reachable over the internet and never updated.

= Kan Slotbaas de gevonden bestanden verwijderen? =

Nee. De openbare versie is bewust rapporterend en schrijft niet naar WordPress-kernmappen, pluginmappen, themamappen, de site-root of een quarantainemap. Herstel de WordPress-kern via het updatescherm en verwijder bevestigde malware via uw hostingbeheer of een deskundige.

= The scan does not progress. =

WP-Cron is not running. Use the **Scan a bit further now** button on the scan page, or set up a system cron job that calls `wp-cron.php`. The plugin never makes loopback requests to your own site, so `DISABLE_WP_CRON` with a system cron job is fully supported and is the recommended setup on a busy shared server.

= I am not receiving any email. =

Check whether the site can send mail at all. On shared hosting `wp_mail()` often does not arrive without an SMTP plugin. Until mail works, a new administrator account simply stays disabled — safe, but you will not notice it.

= Does blocking `?author=N` break my author archives? =

Only if your theme links to authors in that form. With pretty permalinks (`/author/name/`) nothing changes. The rule can be switched off.

= Does blocking xmlrpc.php break anything? =

It breaks the WordPress mobile app, Jetpack and pingbacks. There is a setting to allow it again.

= Will automatic updates overwrite my manual choices? =

No. If you switch a plugin's auto-update off in wp-admin, Slotbaas adds it to its exclusion list and leaves it alone from then on. You can also maintain that list yourself in the settings.

= Does it work on multisite? =

The plugin activates and runs, and auto-update options are written as network options. It has been built and tested for single-site installations; multisite is not officially supported yet.

== Screenshots ==

1. Overview: pending administrators, findings and blocked requests at a glance.
2. A scan finding, showing the reason in plain language, what the file is for, and the first 40 lines.
3. Approving or denying a new administrator account, including how the account came into being.
4. Automatic updates: which plugins and themes are covered and which are excluded.
5. The firewall screen with rule status, IP lists and recent blocks.

== Changelog ==

= 1.6.9 =
* WordPress.org compliance: file and directory locations are resolved through WordPress APIs, including `plugin_dir_path()`, `wp_get_upload_dir()`, `get_theme_root()`, `WPMU_PLUGIN_DIR` and `get_home_path()`.
* The public scanner is report-only and no longer moves, restores, replaces or deletes files.
* Removed writes to WordPress core directories, the site root, `.htaccess`, `wp-content` and `mu-plugins`.
* Core findings now direct administrators to WordPress' own reinstall workflow.

= 1.6.8 =
* Branding: renamed to Slotbaas - Security, Scanner & Firewall, with the matching `slotbaas` slug and text domain.
* WordPress.org compliance: all included quarantine, deletion and checksum-verified core-repair features are free and fully functional; no remediation code is intentionally locked.
* Safety: remediation remains administrator-initiated, nonce-protected and limited by per-file impact checks.
* WordPress.org compliance: the plugin no longer defines `DISABLE_WP_CRON` or writes a rule that disables `wp-cron.php`; system-cron configuration remains the site owner's or host's responsibility.
* Transparency: every WordPress.org endpoint used for checksum detection and explicit core repair is documented with the data sent, Terms of Service and Privacy Policy.

= 1.6.7 =
* Performance: unchanged scanner baselines are now marked with one bulk database update per batch instead of one write per file.
* Performance: scan batches process at most 250 files by default and wait a randomized 5–15 minutes before continuing.
* Performance: reports, update-setting synchronization and log cleanup receive stable per-site time slots to avoid server-wide synchronization spikes.

= 1.6.6 =
* New: administrators see a nonce-protected prompt to add their current trusted IP address to the firewall allowlist.
* Privacy and security: optional HV Media management IP access is explicit, disclosed and disabled by default; disabling it removes those addresses from the allowlist.

= 1.6.5 =
* Fixed: the automatic overload circuit breaker no longer blocks wp-login.php; login and password-reset rate limits continue to protect the endpoint.
* Compatibility: Axios is no longer blocked by default because legitimate webhook and booking integrations commonly use it.

= 1.6.4 =
* WordPress.org-compliance: requestgegevens worden vroeg gesaneerd en publieke CSS wordt via de enqueue-API geladen.
* Beheermeldingen zijn beperkt tot de eigen Slotbaas-schermen.
* De scanner is detectiegericht en schrijft niet meer naar kern-, plugin- of themamappen.
* Vertalingen worden via translate.wordpress.org geleverd.

= 1.6.3 =
* New: Server Shield can block external wp-cron.php requests before PHP and simultaneously disables visit-triggered WP-Cron. This option is off by default and requires a PHP-CLI cron in the hosting panel.

= 1.6.2 =
* Fixed: overload protection no longer returns a blanket 503 for normal public pages; only anonymous login, XML-RPC and admin AJAX endpoints are shed during high server load.

= 1.6.1 =
* Changed: automatic scans and every continuation batch run only between 01:00 and 06:00 in the WordPress site timezone.
* Changed: sites keep a stable minute within the night window; weekly scans are also distributed over different nights.
* Changed: post-update finding revalidation is deferred to the night window instead of running on the first daytime page request.
* Changed: minimum PHP version lowered to PHP 7.4 after removing the unnecessary PHP 8 requirement.

= 1.6.0 =
* New: brute-force protection counts failed logins independently per IP address and per account, with privacy-safe account keys and optional permanent Server Shield blocking.
* New: automatic overload protection defers scans, sheds anonymous login/AJAX traffic when the host is busy, and returns a temporary 503 for anonymous dynamic traffic at critical load.
* Changed: scan start times are distributed across stable per-site slots, busy hosts defer batches, and new installations use weekly five-second scan batches.

= 1.5.1 =
* Changed: automatic scans are spread over a stable per-site time slot instead of synchronizing after bulk activation.
* New: scan batches and scheduled scans automatically defer when the host load is too high.
* Changed: new installations scan weekly by default with a five-second batch budget.

= 1.5.0 =
* New: Server Shield writes a clearly marked, removable block to `.htaccess`, stopping xmlrpc.php, configured bot clients and permanently blocked exact IP addresses before WordPress and PHP start.
* Permanent password-reset blacklist entries are synchronized to Server Shield immediately. Allowlisted addresses and `HVSEC_ALLOW_IP` are never written to the early block.
* Earlier versions used `axios/` as the default early bot signature. Version 1.6.5 stopped doing so because legitimate integrations use Axios; version 1.6.8 also removes that legacy signature automatically during upgrades.

= 1.4.0 =
* New: password-reset abuse protection limits requests independently per IP address and per submitted account before WordPress creates a reset key or sends mail.
* New: optional strict mode permanently adds an abusive exact IP address to the firewall blocklist. Allowlisted addresses and `HVSEC_ALLOW_IP` are never added automatically.
* Privacy: submitted usernames and email addresses are stored only as keyed, non-reversible hashes and are not written to the log.

= 1.3.0 =
* New: a built-in list of files that are known to belong where they are, applied automatically on update. It currently covers Elementor Safe Mode, Health Check troubleshooting mode, the Wordfence firewall loader, Installatron and this plugin's own firewall loader. Existing findings for these files are withdrawn automatically when you update.
* The recognition works on content, not on filename. A file only counts as known when it sits in the expected place, actually declares what it claims to be, and contains no obfuscated code. A shell named `elementor-safe-mode.php` matches none of that and is still reported. An exclusion list based on filename alone would have been exactly the blind spot this plugin exists to prevent.
* The list is shown in full on the settings screen — a security plugin that quietly skips things is one you cannot check — and can be extended with the `hvsec_known_files` filter, for a must-use plugin from your host for instance.
* New: guard files in the upload directories are no longer listed at all by default. Their content has already been verified as unable to execute anything. A new setting brings them back for anyone who wants to see everything.

= 1.2.2 =
* Fixed: a guard file that builds its status line from `$_SERVER['SERVER_PROTOCOL']` — the standard PHP idiom, used by WPForms among others — is recognised as harmless. Only that one server-generated value is allowed, never visitor input, and a second independent check rejects anything containing code that could execute, include or obfuscate.
* Changed: a file in `mu-plugins` that declares itself properly with a plugin name and author, and contains no obfuscated code, is reported for review instead of as critical, with its declared name shown. Malicious files in that directory rarely announce themselves — they do not need to, since everything there loads regardless. Copying a header does not help: files claiming to be a known plugin while containing `eval`, `gzinflate`, `str_rot13`, a shell call, a variable function name or backticks stay critical. A file with no header at all also stays critical.

= 1.2.1 =
* Fixed: the protective `index.php` that plugins place in the directories they create is no longer reported as critical. Half the WordPress ecosystem writes a small guard there — a forbidden header and an exit, nothing more — and only files consisting purely of comments were recognised as harmless before. A file is now judged on whether its code can do anything at all: any variable, call or include and it is treated as a normal finding again. Verified against disguised shells such as `exit; ?><?php eval($_POST['x']);`, which stay critical.

= 1.2.0 =
* New: detection of a second WordPress installation in a subdirectory. An archived copy of a site in the web root is reachable over the internet and is never updated, which makes it a well-known way in. Reported once, clearly, instead of as hundreds of findings about its files.
* Fixed: files with no executable code are never reported for a back-dated date. The empty `index.php` that WordPress and many plugins place in every directory could not be a backdoor and should never have been flagged.
* Fixed: the back-dated date rule now compares against the whole installation instead of a single directory. A file that is the only PHP file in its folder — `wp-content/plugins/index.php`, for instance — had no neighbours to compare with and was therefore always reported.
* Fixed: a file no longer counts as its own neighbour in that comparison.
* Fixed: date checks are switched off inside an archived installation, where dates say nothing. The other rules still apply there, so a shell in an archive is still found.
* Changed: findings are re-evaluated automatically at the end of every scan, when the full picture of the installation is available. Findings recorded early in a scan are no longer judged on incomplete information.
* Changed: the plugin never moves or deletes whole directories. A finding about a directory explains what to do instead.

= 1.1.0 =
* New: automatic updates for plugins, themes, translations and WordPress core, kept switched on when something new is installed. Manual choices made in wp-admin are respected and adopted.
* New: **Repair** for modified core files. The original is fetched from WordPress.org, verified against the official checksum, and the previous file is kept in quarantine. Core files can no longer be deleted from the findings list.
* New: impact analysis on every finding — the plugin now states what a file is for and refuses actions that would break the site.
* New: bulk quarantine and bulk deletion, with files the site needs skipped and reported back.
* New: **Re-evaluate findings**, which withdraws findings that no longer trigger under the current rules and removes their log entries. It runs automatically when the plugin is updated.
* New: coverage per area on the scan page, so you can see that the root directory, wp-admin and wp-includes were included.
* Fixed: core files are now compared against both the site locale and the en_US package. A site installed in English and later switched to another language no longer reports `wp-includes/version.php` as modified.
* Fixed: the back-dated modification time rule only fires on a file whose date stands out from its neighbours. A migration or a restore no longer produces dozens of false alarms.
* Fixed: files that appeared during a known update run are no longer reported as back-dated. WordPress preserves archive dates when unpacking a ZIP, which used to trigger the rule after every update.
* Changed: the notification email address is empty by default and must be filled in. It no longer defaults to the author's address.
* Changed: the token signature is no longer needlessly truncated; `wp_hash()` returns 32 characters.

= 1.0.0 =
* First release: approval for new administrators, batched file scanner with quarantine, and a fail-open firewall.

== Upgrade Notice ==

= 1.6.1 =
Restricts all automatic scan work to a distributed local night window and supports PHP 7.4.

= 1.6.0 =
Adds brute-force protection, coordinated scan scheduling and an automatic server overload circuit breaker.

= 1.5.1 =
Prevents synchronized scans from overloading a shared server when Slotbaas runs on many WordPress installations.

= 1.5.0 =
Adds Server Shield, an early webserver layer that blocks known abusive traffic before WordPress starts.

= 1.4.0 =
Adds rate limiting for password-reset requests and an optional strict IP blacklist mode.

= 1.3.0 =
Known vendor files such as Elementor Safe Mode and the guard files plugins place in their upload directories no longer appear as findings. Existing findings for them are withdrawn on update.

= 1.2.2 =
Recognises the guard files plugins place in their upload directories, and no longer treats a properly declared must-use plugin such as Elementor Safe Mode as critical.

= 1.2.1 =
Stops reporting the protective index.php files that plugins place in their own upload directories as critical.

= 1.2.0 =
Removes the remaining sources of false alarms around file dates and withdraws existing incorrect findings automatically. Adds detection of a second WordPress installation left behind in the web root.

= 1.1.0 =
Fixes two sources of false alarms (core files after a language switch, and back-dated dates after a migration) and withdraws existing incorrect findings automatically on update. Adds automatic updates, core file repair, and bulk actions with a safety check.
