cPanel Malware Scan False Positive? How to Confirm It
A malware scanner flagging a plugin file you just installed yourself is not automatically wrong, but it is not automatically right either. Here is how to actually check.
A cPanel malware scan false positive usually shows up the same way every time: a scanner like ClamAV or Imunify flags a file inside a plugin, theme or library you recognize, and it is genuinely unclear whether that file is legitimate code that happens to match a signature, or an actual infection hiding inside something you trust. Guessing wrong in either direction costs you: delete a clean file and break the site, ignore a real one and the compromise spreads.
Read the actual detection name
Every scanner reports a signature name alongside the flagged file, and that name tells you how confident to be before you do anything else.
clamscan -v --infected /home/USERNAME/public_html/wp-content/plugins/theplugin/file.php
| Detection type | What it means | False positive risk |
|---|---|---|
Named malware family, e.g. PHP.Backdoor.WSO | Matches a known, specific malicious code pattern | Low, worth treating as real until proven otherwise |
Heuristic or generic, e.g. PHP.Generic-9821 or Heuristics.Encrypted | Matches suspicious characteristics, not a specific known payload | Higher, common in minified or obfuscated legitimate code |
PUA (Potentially Unwanted Application) | Matches functionality that could be misused, not confirmed malicious | High, often flags legitimate admin or backup tools |
Compare against a known-clean copy
This is the single most reliable check available, and it takes minutes. Download the exact same version of the plugin or theme from its original source (the WordPress.org repository, the vendor's official site) and diff it against the flagged file.
wget https://downloads.wordpress.org/plugin/theplugin.1.2.3.zip
unzip theplugin.1.2.3.zip -d /tmp/clean
diff /tmp/clean/theplugin/file.php /home/USERNAME/public_html/wp-content/plugins/theplugin/file.php
No output means the files are identical, the scanner's match is a false positive against that specific plugin version, not evidence of a compromise. Any diff output at all is worth reading carefully, since even a few injected lines at the top or bottom of an otherwise normal file is the classic shape of a real infection hiding inside legitimate code.
Check the modification time against the install date
A file whose timestamp lines up exactly with when you installed or last updated the plugin is far less suspicious than one modified independently, days or weeks after the plugin's last legitimate update.
find /home/USERNAME/public_html/wp-content/plugins/theplugin -type f -printf '%T@ %p\n' | sort -n | tail -20
If every file in the plugin shares the same timestamp except the one flagged, that outlier is worth treating as a real finding regardless of what the diff shows, since an attacker who successfully touched only one file in an otherwise untouched directory is a very specific, deliberate pattern.
Look for the actual red flags, not just the scanner's opinion
Independent of what any single scanner concluded, these patterns inside the flagged file are worth treating seriously on their own.
eval(base64_decode(...))or similar decode-then-execute chains, which have essentially no legitimate use in a WordPress plugin or theme file.- A file named to look like a core file but sitting in the wrong directory, for example
wp-config.phpoutside the WordPress root. - Outbound network calls (
curl,fsockopen,file_get_contentsagainst an external URL) inside a file that has no legitimate reason to make one, like a static contact form template. - Code that only activates under a specific request parameter, a common way backdoors stay dormant until an attacker calls them directly.
Running this diff-and-verify process by hand, once, on one flagged file is manageable. Doing it every time a scanner flags something across a fleet of hosted accounts, and remembering to check file ownership and timestamps every time, is where it stops scaling. BashSecure runs this same comparison against known-clean plugin sources automatically and separates confirmed findings from low-confidence heuristic matches before it ever asks you to act on one.
Triaging a flagged plugin file automatically
Compared the flagged file against the official WooCommerce 8.4.1 package. The file differs from the clean version by 14 lines appended at the end, added 2 days after the plugin's last legitimate update, containing a base64-encoded eval call. This matches a known backdoor pattern, not a false positive.
| Check | Result |
|---|---|
| Diff against official source | 14 lines added |
| Modification time vs plugin install | 2 days after, outlier |
| Decode chain present | yes, eval(base64_decode()) |
Restoring the file to the official 8.4.1 version and rescanning the full plugin directory for any other modified files before confirming.
cp /tmp/clean/woocommerce/file.php /home/client-shop/public_html/wp-content/plugins/woocommerce/file.php
Try this on one of your own servers.
Start free trialVerify the outcome either way
If you conclude it is a false positive, do not just move on silently, confirm your reasoning holds up before you whitelist anything.
sha256sum /home/USERNAME/public_html/wp-content/plugins/theplugin/file.php
sha256sum /tmp/clean/theplugin/file.php
Matching hashes are the strongest possible confirmation. Only whitelist the exact file path and version in your scanner's exclusion list once you have this, and re-verify the exclusion after every plugin update, since a whitelisted path does not protect you from a genuinely malicious file later placed at that same location.
Prevent repeat false positives
- Keep plugins and themes updated directly from their official source, unofficial or nulled copies are both a security risk and a common source of scanner false positives from bundled tracking or ad code.
- Maintain a whitelist scoped to exact file paths and versions, not entire plugin directories, so a future update does not inherit an old exclusion blindly.
- Re-run the diff check after every plugin update rather than assuming last month's clean result still holds.