api.wordpress.org/core/checksums/1.0/ for core (MD5) and downloads.wordpress.org/plugin-checksums//.json for plugins (MD5 and SHA-256). Each file gets one of five verdicts.
How to access: GuardForge > File integrity. Runs daily by cron and on demand.
Free/PRO: Free, including the plugin and theme walk. The integrity_scope_extras setting decides the scope; there is no licence check anywhere in it.
The five verdicts:
official— the file matches the published hash.modified— it is in the published build and does not match.missing— the build has it and this site does not.unexpected— a file that is in no published build at all. This is the one a snapshot-to-snapshot comparison can never produce: a shell that was already on disk when the baseline was taken looks exactly like an unchanged file. Findings are sorted withunexpectedfirst.unreferenced— nothing publishes hashes for it. Every theme, every premium or custom plugin, and any single-file plugin whose wordpress.org slug cannot be derived from its filename. These keep the scan-to-scan comparison and are never counted as clean.
- A 404 from wordpress.org means “no reference”, never “clean”. Reading it as a pass would put a tick beside exactly the files an attacker is likeliest to be living in.
- Answers are cached per (slug, version): 30 days for a catalogue that exists, 3 days for one that does not — wordpress.org publishes a plugin’s sums slightly after the release, so on release day “not yet” and “never” are indistinguishable. A transport failure is not cached at all; otherwise one timeout would declare the whole of core unreferenced for a month.
- One scan makes at most ten requests. What it could not reach is queued for the next run and named on the screen, so a site with sixty plugins is covered over a few days rather than in one burst a cron tick cannot survive.
- The request carries the version, the locale and the slug — not the site address. Guard Forge deliberately keeps the site URL out of the user agent so that sentence can be written down.
- The hardening score’s
integrity_baselinerow stops reading “Done” whileunexpected > 0. - All of it goes through the shared
Forge_NetSSRF gate, andclass-staging-outbound-surface-test.phpenumerates every way out of the free package so a new one cannot be added silently.