WP Manifestindependent plugin directory
manifest / developer / wpshadow

Shadow by Christopher Ross WP Registry grade D+. High-severity findings · 3 findingsWP RegistryHigh-severity findings · 3 findingsOpen the reportD+

WordPress diagnostics and site audits: 230+ automated checks for performance, security, SEO, and accessibility with safe remediation.

★ 3stars
25release downloads
v1.6165.1latest · 4mo ago

Install

Download thisismyurl-shadow-1.6165.zip From the v1.6165.1 release on GitHub, Jun 14, 2026

Upload it under Plugins → Add New Plugin → Upload Plugin, then activate.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/thisismyurl/wpshadow/releases/download/v1.6165.1/thisismyurl-shadow-1.6165.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Shadow

Shadow is a local-first WordPress diagnostics and remediation plugin.

It runs on your server, reports what it finds, and where it can, fixes it. No account, no external service, no data leaving the site.

What ships today

Shadow groups its work into a few areas:

  • Diagnostics across categories like performance, security, configuration, and content health. Each one reports a finding you can read and act on.
  • Treatments that act on findings: most run automatically, the rest give you step-by-step guidance for the ones a plugin shouldn't touch on its own.
  • Dashboard and findings views, plus WordPress Site Health integration so the results show up where you already look.
  • File-write review, local backup, and recovery workflows for anything that changes files on disk.
  • Activity logging and KPI tracking, with admin behavior that understands multisite.
  • Top-level runtime wrappers and WP-CLI commands for running Shadow without the dashboard.

For current diagnostic and treatment counts, see docs/FEATURES.md — those numbers change as the registry grows and any figure hardcoded here goes stale within a release.

Beta scope

Shadow is in beta. Here's the honest line between what's solid and what isn't.

In scope and working: diagnostics, the treatments that ship enabled, the dashboard and findings views, Site Health integration, local backup and recovery, logging, and the WP-CLI surface.

Not yet: some treatments are guidance-only on purpose, because automating them on a live site is riskier than the problem they solve. Those are labeled in the findings view. The remediation set will keep growing, and the API around treatment classes may still shift between beta releases.

If you're running Shadow on a production site, take a backup first and read the finding before you apply a treatment.

Non-negotiable ideas

A few principles built into the design:

  • Local-first. Shadow does its work on your server. It does not phone home, and it does not need an account to function.
  • Read before you write. Every treatment that changes a file goes through write review and is backed up locally first, so you can undo it.
  • The site owner stays in control. Anything Shadow can't safely automate becomes guidance, not a silent change.

Quick start

Site owners

  1. Install and activate the plugin.
  2. Open the Shadow dashboard.
  3. Run a scan, read the findings, and apply treatments one at a time.

Take a backup before applying treatments on a production site.

Contributors

  1. Clone the repo.
  2. composer install
  3. composer test:smoke for a fast pass, or composer test:phpunit for the full suite.

If your environment needs an explicit PHP binary for PHPUnit:

php8.3 ./vendor/bin/phpunit --configuration phpunit.xml.dist

See CONTRIBUTING.md for the longer version, including coding standards and how the diagnostic and treatment registries are structured.

WP-CLI

wp wpshadow diagnostics list
wp wpshadow diagnostics run <diagnostic>
wp wpshadow scan run
wp wpshadow treatments list
wp wpshadow treatments apply <finding>
wp wpshadow readiness export

Documentation map

Source of truth

When you need exact numbers, read them from the code, not from notes or this README. The authoritative sources are:

  • Diagnostic_Registry::get_diagnostic_definitions() for diagnostics
  • Treatment_Metadata::get_counts() for treatments

Planning documents drift. These methods don't.

Accessibility and privacy

Shadow is local-first. No account is required, and it makes no unexpected third-party requests. What runs on your site stays on your site.

See docs/ACCESSIBILITY.md, PRIVACY.md, and docs/BUSINESS_MODEL.md.

Versioning

Versions follow X.Yjjj.hhmm — year, Julian day, 24-hour time of the build.

About

Shadow is built and maintained by Christopher Ross. I've been building on WordPress since 2007 and I build tools like this because the same problems keep showing up on real sites — and a small, focused plugin is usually the right fix. No tracking, no ads.

WordPress.org: profiles.wordpress.org/thisismyurl · GitHub: github.com/thisismyurl · LinkedIn: linkedin.com/in/thisismyurl

Support and sponsorship

Shadow is free and stays free. If it saved you time and you want to give something back, sponsorship helps me keep maintaining it: GitHub Sponsors, Bitcoin, Dogecoin, PayPal, or Interac e-transfer.

Found a bug? Open an issue on the Issues tab. Have a question? The Discussions tab is the place for it.

License

GPL-2.0-or-later. See LICENSE.


This project follows the 10 Core Pillars.

Releases

6 releases. Each count is every asset in that release; expand a row for the breakdown.

Tag
Published
Assets
Downloads
v1.6165.1 latest
Jun 14, 2026 4mo ago
thisismyurl-shadow-1.6165.zip
25
Jun 14, 2026 4mo ago
thisismyurl-shadow-1.6165.zip
0
May 27, 2026 5mo ago
tag only
—
May 23, 2026 5mo ago
tag only
—
May 5, 2026 5mo ago
tag only
—
Apr 5, 2026 6mo ago
tag only
—
D+ grade

Security

Shadow by Christopher Ross 1.6165 · audited by WP Registry

High-severity findings.

1 high 2 medium
Audited release
1.6165
Findings
3
Worst severity
high
Content hash
c13577d2fbca8065e3d0947f…

Findings

  • high Login URL hardening treatment bricks wp-login.php

    insecure_config

    Applying the shipped login-url-hardening treatment stores a 16-character mixed-case token in thisismyurl_shadow_login_url_token and then rejects every wp-login.php request whose GET wpstoken does not hash_equals that value. Two independent bugs make a successful login impossible: (1) WordPress core login/logout/lost-password forms post to site_url('wp-login.php') / site_url('wp-login.php','login'), which do not run the login_url filter, so the token is dropped on POST and on logout/reset URLs; password-reset emails from core also omit it. (2) on_login_init runs the supplied token through sanitize_key() (strtolower) before comparing it to the stored mixed-case value from random_bytes/base64, so even a correctly bookmarked GET URL fails whenever the token contains uppercase letters. Observable consequence: after an administrator clicks Fix on this finding, wp-login.php 302s to the homepage for everyone, including the current admin after their session expires. Recovery requires deleting the option via WP-CLI or the database. The UI confirmation toggle is not enforced in apply().

    includes/systems/core/class-treatment-hooks.php:302-318

    Recommendation

    Do not gate wp-login.php solely via login_url. Append the token on the site_url/network_site_url 'login' and 'login_post' schemes (or rewrite wp-login.php), preserve it on logout and reset URLs, compare the raw GET value with hash_equals against a lowercase hex/base32 secret, and refuse to enable the gate until apply() verifies the confirmation input. Add an emergency bypass (define in wp-config) documented in the treatment message.

  • medium Login URL secret is leaked on every public login_url() output

    information_disclosure

    Treatment_Hooks::filter_login_url unconditionally appends wpstoken=<secret> to every URL produced by wp_login_url(). That helper is used by wp_loginout(), comment forms, many themes' header/footer login links, and third-party plugins. Once the treatment is enabled, the 'secret' that is supposed to hide wp-login.php is present in public HTML and in any email that goes through login_url. Combined with the lockout bugs, this both advertises a token that does not actually unlock login and, if the comparison is later fixed without changing the leak, would let any visitor recover the gate token from page source.

    includes/systems/core/class-treatment-hooks.php:281-287

    Recommendation

    Stop attaching the token to globally filtered login_url() output. If a hide-login control is required, use a rewrite slug that is never printed on the front end (WPS Hide Login model), or inject the token only into admin-generated bookmark copy shown once after apply().

  • medium Login throttle and comment rate-limit trust client-controlled forwarding headers

    dos

    When login-throttling-active or form-rate-limiting-active is applied, Treatment_Hooks::get_visitor_ip() returns the first IP in HTTP_CF_CONNECTING_IP or HTTP_X_FORWARDED_FOR without confirming the request actually came from Cloudflare or a trusted proxy. An unauthenticated attacker can send five failed logins with X-Forwarded-For set to an administrator's IP and lock that IP out for one hour (authenticate returns WP_Error thisismyurl_shadow_login_lockout), or rotate the header to bypass the 5-attempt / 15-minute window. The same helper keys comment flood transients, so anonymous comment limits are equally spoofable. Login throttling is classified risk=safe and is auto-applied by Scan_Frequency_Manager::apply_automatic_treatments() on Guardian runs when run_treatments is true (the default in get_scan_config()).

    includes/systems/core/class-treatment-hooks.php:871-884

    Recommendation

    Use REMOTE_ADDR unless the site has an explicit trusted-proxy setting (or Cloudflare is detected via authentic CF headers / connecting IP ranges). Never take the left-most X-Forwarded-For hop by default. Document that login throttling is not safe to auto-apply on hosts without a real proxy hop.

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.