diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b80622..19ae6b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,70 @@ All notable changes to JayShield are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## 1.3.0 - 2026-09-14 + +A release about being usable on a real site, and honest about what a scan +means. + +### Added + +- WordPress awareness. A scan recognises a WordPress install and prints its + version, and every flagged file that belongs to WordPress itself is tagged + `config`, `core`, `theme`, or `plugin` in the report and in `--json`. Those + files carry different advice: replace them with a clean copy of the same + version rather than only removing them, because quarantining one takes the + site or the theme offline. Scanning only `wp-content` still knows its site. +- `--include ` scans folders that are skipped by name, and + `--include all` scans everything. The report now says how many folders were + skipped and which names, so a clean result is never quieter than it should + be. Skipped names are also listed in `--help`. +- A clear answer when given a website address. `jayshield https://example.com` + used to fail with "no such file or folder"; it now explains that JayShield + reads files on disk and shows the two ways to scan that site, over SSH or on + a downloaded copy. +- README: a Requirements section, a step-by-step guide to scanning a WordPress + site on any hosting, an after-the-scan checklist for the things a file + scanner cannot do, and a plain list of what JayShield can and cannot promise. +- `scan()` accepts `includeDirs` and returns `sites`, `skippedDirNames`, and + `stats.skippedDirs`; each infected record may carry `site`. The site helpers + and the walker are exported from the package. + +### Changed + +- Folders named `cache` are now scanned. On WordPress, `wp-content/cache` is + web-served, writable, and a common place to plant a shell, and the upload + heuristic already treated it as one, so skipping it left a blind spot. +- The upload heuristic no longer flags a cached page whose only PHP is the + `` guard that WP Super Cache writes. A payload after the + guard is still caught. +- The clean-scan line now reads "Nothing in the files scanned matched a known + technique, heuristic, or bad hash", which is what a clean result means. + +### Fixed + +- A pristine WordPress 7.1 download was reported as 26 infected files, five + of them critical. Every one was a false positive, and every one is fixed: + - The WSO webshell rule matched the letters w-s-o inside any word, so + "Dawson", "WSODs", `useNewSodiumAPI`, and a certificate bundle were all + called a webshell. It now matches WSO's own function and constant names. + - The hex-escape rule flagged every long binary constant, in sodium_compat, + getID3, and SimplePie. It now flags a dangerous function name or request + variable spelled in hex, or a hex string literal called as a function, + and is high severity because that is never innocent. + - The hidden iframe rule matched `marginwidth="0"`, a hidden scratch frame + with a `javascript:` source, and the Google Tag Manager noscript snippet + that sits in most theme headers. It now requires a hidden frame with a + real destination, and a page full of unterminated tags can no longer + stall a scan. + - The long-line, high-entropy, and base64-blob heuristics fired on SVG + icons, entity tables, arrays of class names, and an embedded WebAssembly + module. Each now also requires a decoder, an execution call, or request + input on the same file or line, which a packed payload cannot do without. +- Verified clean with the new rules against WordPress 7.1 with and without + `--include vendor`, WooCommerce 11.1.0, the Astra theme 4.13.11, jQuery + 3.7.1, and Laravel 12.x, while every planted threat in the test fixtures is + still found. + ## 1.2.1 - 2026-07-13 ### Changed diff --git a/README.md b/README.md index 4024458..d56102d 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,18 @@ site stops serving them right away. It is one small tool with no dependencies, so you can trust it, read it, and run it anywhere Node is installed. +## Requirements + +- **Node.js 18 or newer** on the machine that runs the scan. Check with + `node -v`. If that prints nothing or an older version, install Node from + [nodejs.org](https://nodejs.org/) (macOS, Windows, and Linux installers). +- **The site's files on that machine.** JayShield reads files on disk. It does + not connect to a website address, so `jayshield https://example.com` is + refused with an explanation. See [Scan a WordPress site](#scan-a-wordpress-site) + for the two ways to get the files in front of it. + +Nothing else: no account, no PHP, no database access, no background service. + ## Quick start No install required, run it straight from npm: @@ -57,6 +69,81 @@ jayshield ./public_html The examples below write `jayshield` for short, meaning however you choose to run it. +## Scan a WordPress site + +JayShield finds the files an attacker planted or changed. To do that it needs +the site's files, so pick whichever of these fits your hosting. + +**Path A: run it on the server.** If your host gives you SSH (most VPS plans, +managed WordPress hosts, and many shared hosts), log in and check for Node: + +```bash +ssh you@your-server +node -v +``` + +If Node is there, scan the site root, which is usually `public_html`, `htdocs`, +`www`, or `/var/www/`: + +```bash +npx @jayhackpro/jayshield ~/public_html +``` + +If Node is missing and you cannot install it, use Path B. On many shared hosts +you can install Node without root through cPanel's "Setup Node.js App" or with +[nvm](https://github.com/nvm-sh/nvm). + +**Path B: scan a downloaded copy.** This works on any hosting, including +hosting with no SSH at all. Download the whole site folder with an SFTP client +(FileZilla, Cyberduck, WinSCP), with the "compress and download" button in your +host's file manager, or from a full backup made by your backup plugin. Unzip it +on your own computer and scan the folder: + +```bash +npx @jayhackpro/jayshield ~/Downloads/example.com +``` + +The report is about the copy. Nothing on the live site changes, so clean the +live site by hand using the paths in the report: delete planted files over SFTP +and replace infected WordPress files with clean copies (see below). + +**Scan the whole site root, not just `wp-content`.** Attackers also edit +`wp-config.php`, `.htaccess`, and the root `index.php`, and JayShield can only +report on what it is pointed at. For a deeper scan add `--include vendor` so +plugin dependency folders are read too. + +**Reading the results on a WordPress site.** JayShield recognises a WordPress +install, prints its version, and tags each flagged file that belongs to +WordPress itself: + +- **No tag** (`wp-content/uploads`, `wp-content/cache`, a stray file in the + root): almost always planted. Quarantine it. +- **`[wordpress config]`**, **`[wordpress core]`**, **`[wordpress theme]`**, + **`[wordpress plugin]`**: a real file with injected code. The whole file is + untrusted, so replace it with a clean copy of the same version (core and + plugins from wordpress.org, the theme from where you got it) rather than only + removing it. Quarantining one of these takes the site or the theme offline + until you do; `--restore` puts it back if that happens. + +**After the scan.** Removing the files is the start, not the end. Do these too, +because JayShield cannot: + +1. Update WordPress, every plugin, and every theme, and delete the ones you do + not use. The way in was almost always an outdated or nulled one. +2. Change every password: WordPress admins, hosting panel, SFTP, and the + database user in `wp-config.php`. Rotate the salts in `wp-config.php` with + [new values](https://api.wordpress.org/secret-key/1.1/salt/). +3. Look for admin users you did not create, in Users, and remove them. +4. Check the database, which JayShield never reads: `siteurl` and `home` in + `wp_options`, unknown entries in `wp_options`, and `