Skip to content

For Developers

github-actions[bot] edited this page Nov 3, 2026 · 1 revision

Hooks, filters and constants

Name Type Use
wp2ss_signatures Filter Add or change indicators (fixed PHP versions, code patterns, hashes, IPs, log patterns…). Receives the array from includes/signatures.php.
wp2ss_step_budget Filter Seconds of work per slice (float). Lower it on very strict hosts.
wp2ss_scan_complete Action Fires with ( $report, $previous_report ) when a scan finishes
wp2ss_fix_applied Action Fires with the fix record after a one-click fix (quarantine or admin removal)
wp2ss_fix_undone Action Fires with the fix record after a restore or undo
wp2ss_fleet_private_hub Filter Return true to let this member report to a hub on a private network address
WP2SS_FLEET_PRIVATE_HUB Constant Same as the filter, from wp-config.php
WP2SS_DISABLE_UPDATER Constant define( 'WP2SS_DISABLE_UPDATER', true ); in wp-config.php stops all requests to GitHub

Add your own indicators

add_filter( 'wp2ss_signatures', function ( $sig ) {
    // [ severity, regex, description ]
    $sig['code'][] = array( 'high', '/my_known_bad_function\s*\(/i', 'Our internal backdoor marker' );
    $sig['sha1']['0123456789abcdef0123456789abcdef01234567'] = 'Sample from our incident';
    // If PHP ships another fix, tell the scanner about it:
    $sig['php_fixed']['8.4'] = '8.4.28';
    // …or about a newly fixed distribution package: [ prefix, first fixed build ]
    $sig['php_fixed_pkg'][] = array( '8.2.29-1~deb12u', 2 );
    return $sig;
} );

Post every result to Slack

add_action( 'wp2ss_scan_complete', function ( $report ) {
    wp_remote_post( 'https://hooks.slack.com/services/XXX', array(
        'body'    => wp_json_encode( array( 'text' => "{$report['site']}: {$report['verdict']} (PHP {$report['php']['version']})" ) ),
        'headers' => array( 'Content-Type' => 'application/json' ),
    ) );
} );

Report format (JSON)

What Download report (JSON) and wp jat-shell scan --format=json produce:

Field Type Meaning
id string Unique ID of this scan
site string Site URL
wp string WordPress version
php object version, sapi (server type), source (web, web-snapshot or cli)
plugin string Scanner version
source string manual, cron or cli
args object core, log, error_log — what this scan was asked to do
scanned string Finish time, ISO 8601 UTC
duration int Seconds from start to finish
verdict string CLEAN, REVIEW, VULNERABLE, SUSPICIOUS, COMPROMISED
stats object files, php_files, dirs, unreadable, links, large, errors
findings[] array See below
ignored array IDs you ignored (dashboard download only)
findings[] field Meaning
id Stable 12-character ID (use with wp jat-shell ignore)
severity critical, high, medium, low, info
type php, version, plugin, mu-plugin, user, rest, file, code, hash, core, log, scan
message What was found
advice What to do about it
location File path, user:<ID>, role:<name>, rest:<namespace>, or empty
sha1 Fingerprint of the file, where relevant (so an ignored file that changes is flagged again)

Development, tests and releases

# Integration tests — they plant FAKE malware files, so only use a throwaway site
tests/integration.sh /path/to/throwaway/wordpress

# False-positive check — signatures vs. legitimate code
php tests/false-positives.php /path/to/wordpress /path/to/some/plugins

# Mermaid diagrams in this README
npm i mermaid@11 jsdom && node tests/check-mermaid.mjs README.md

What CI checks on every push

Job What
PHP syntax Every file on PHP 7.0 – 8.5
Integration Real WordPress + MySQL: latest WP on PHP 7.4 and 8.4; WP 7.0, 7.0.1 and 6.9.4 (vulnerable); WP 6.4 on PHP 7.0. ~90 checks: PHP2Shell version logic and log evidence, wp2shell indicators, false-positive bait (Wordfence, Performance Lab, cache plugins…), evasion (padded files, symlinks, images, odd file names, hiding in the scanner), resumable slices, locking, cancellation, crash isolation, signed ignore list, admin baseline, self-integrity, exit codes. Fails on any PHP notice.
False positives Signatures vs. WordPress core, WooCommerce, Yoast SEO, Elementor, Jetpack, Contact Form 7, WP Super Cache — zero High/Critical allowed
Docs Every Mermaid diagram in this README must parse

How a release happens

flowchart LR
    A["Bump version in<br/>jat-shell-scanner.php ×2<br/>+ readme.txt"] --> B["Add CHANGELOG.md entry"]
    B --> C["Push to main"]
    C --> D["CI: lint, integration,<br/>false positives, docs"]
    D -- all green --> E["Release workflow:<br/>versions match? new version?"]
    E -- yes --> F["Zip + manifest.json + checksum<br/>tag vX.Y.Z, GitHub release"]
    F --> G["Every installed site<br/>sees the update within ~12h"]
    F --> I["Deploy workflow:<br/>WordPress.org build → SVN trunk + tag"]
    D -- any red --> H["Nothing is released"]
Loading

WordPress.org build. bash .github/scripts/build-wporg.sh writes build/jat-shell-scanner/ and a zip of it: the same files minus the GitHub updater and the Update URI header. CI runs the official Plugin Check on that build. Once the plugin is approved on WordPress.org and the SVN_USERNAME / SVN_PASSWORD repository secrets are set, each GitHub release is also committed to WordPress.org SVN (trunk and tag), with the banner, icon and screenshots from .wordpress-org/.

Manual WordPress.org workflows (Actions tab → Run workflow): Deploy to WordPress.org publishes the current version again, and Update WordPress.org readme publishes only readme.txt and the .wordpress-org/ images, without a new version.

Wiki. The wiki is built from this README by .github/scripts/build-wiki.py, which splits the numbered sections into pages. The Wiki workflow rebuilds it whenever README.md changes on main, so edit the README, not the wiki.

Workflow actions are pinned to exact commits. Detection rules live in includes/signatures.php — new indicators are very welcome: open an issue or pull request with a link to the source.

Security

Found a security problem in this plugin? Please report it privately via Security → Report a vulnerability on this repository — see SECURITY.md. New indicators aren't secret; share them as a normal issue or pull request.

Clone this wiki locally