Skip to content

Sightread Signal Reference

github-actions[bot] edited this page Oct 29, 2026 · 1 revision

Every signal Sightread can raise: what makes it fire, its usual false alarms, how to review a finding and the incidents it is built on. Each signal has its own anchor, so Sightread-Signal-Reference#remote-fetch-exec opens one; reports, SARIF rules and explain link here. Verified against rhythm-sightread 1.0.0, whose docs/signals.md this page is generated from.

rhythm sightread signals lists them in the terminal and rhythm sightread explain <id> prints one. Severity is the default before the context rules raise or lower it; signals.severity in the config changes it for one project. Confidence is confirmed for an exact pattern and likely for a heuristic; --min-confidence confirmed keeps the likely ones out of the verdict and the gate.

A signal fires when a capability is in the new version and not in the installed one, so code a plugin already had stays quiet. Five signals can never be ignored, allowed or lowered: integrity-mismatch, version-republished, approved-version-changed, archive-path-escape and known-malicious.

All signals

ID Severity Confidence Ecosystems Title
integrity-mismatch critical confirmed all Archive does not match the published hash
version-republished critical confirmed npm, composer Same version, different bytes than the lockfile recorded
approved-version-changed critical confirmed all Allowed version now has a different hash
known-malicious critical confirmed npm, composer Listed as malicious by OSV
wporg-closed critical confirmed wp-plugin, wp-theme Closed on WordPress.org
archive-path-escape critical confirmed all Archive entry points outside the package
remote-fetch-exec critical confirmed all Fetches remote code and runs it
decode-exec critical confirmed all Decodes hidden code and runs it
admin-user-hidden critical confirmed wp-plugin, wp-theme Hides users from the admin user list
lockfile-source-change high confirmed npm, composer Lockfile entry moved off the registry
archive-link high confirmed all Archive holds a link or special file
archive-collision high confirmed all Archive holds two entries with the same name
unanalysed high confirmed all Code that could not be analysed
eval-dynamic high confirmed all Runs code built at run time
obfuscation high likely all Adds a large encoded or scrambled blob
input-unserialize high likely wp-plugin, wp-theme, composer Unserializes request or remote data
write-outside-package high likely all Writes files outside its own folder
admin-user-create high confirmed wp-plugin, wp-theme Creates an administrator account
capability-change high likely wp-plugin, wp-theme Grants admin capabilities to another role
update-suppression high likely wp-plugin, wp-theme Blocks updates of other plugins or themes
bot-cloaking high likely wp-plugin, wp-theme, npm Shows search engines different content
credential-access high likely all Reads credentials or tokens
data-exfil high likely all Sends site data to a new host
code-in-asset high confirmed all Hides code in an image, font or text file
npm-install-script-new high confirmed npm Adds or changes an install script
composer-plugin-new high confirmed composer Runs code when Composer installs it
owner-change high confirmed all New owner or publisher
provenance-dropped high confirmed npm Release lost its build provenance
typosquat high likely npm, composer New dependency named like a popular package
known-vulnerable high confirmed npm, composer Has a known vulnerability
dependency-confusion critical confirmed npm, composer Private name also published publicly
outbound-new-domain medium confirmed all Contacts a new domain
rest-open-route medium likely wp-plugin, wp-theme Adds a REST route anyone can call
changelog-mismatch medium likely all Changelog does not mention what the code adds
dependency-new medium confirmed npm, composer Adds a runtime dependency
composer-autoload-files medium confirmed composer Adds a file that runs on every request
fresh-release medium likely all Released very recently
dormant-release medium likely all First release after a long silence
git-tag-missing medium likely npm No matching tag in the repository
size-jump medium likely all Code grew far more than usual
unicode-hidden medium confirmed all Hidden or bidirectional characters in code
secret-added medium confirmed all Adds a credential to the code
cron-new-hook low confirmed wp-plugin, wp-theme Schedules a new background task
license-change low confirmed all License changed
abandoned low confirmed wp-plugin, wp-theme, composer Marked abandoned or not updated in years
local-drift info confirmed wp-plugin, wp-theme, composer Installed copy differs from the official one
php-requirement info confirmed wp-plugin, wp-theme, composer PHP requirement changed
files-removed info confirmed all Removes security-relevant files

Context rules

The scorer adjusts a finding's severity by what else is in the same update, in this order:

  1. Owner change raises. With owner-change in the same update, eval-dynamic, obfuscation, input-unserialize, write-outside-package, admin-user-create, capability-change, outbound-new-domain, data-exfil and dormant-release go up one level (at most critical). The finding says raised by owner-change.
  2. Same function raises. Two different code signals in one function raise the lower one a level, with raised by <other id>.
  3. Vendored paths lower. Findings under tests/, test/, docs/, examples/, a node_modules/ inside a package or vendor/ inside a WordPress plugin go down one level, as does obfuscation in *.min.js or *.bundle.js. The finding is marked vendored path. Critical never goes below high and the five signals above are never lowered.
  4. Confidence gate. With --min-confidence confirmed, likely findings stay in the JSON with below_confidence: true but don't count.
  5. Order. Findings sort by severity, then confidence, then file and line, so output never depends on timing.

integrity-mismatch

Archive does not match the published hash

  • Severity: critical (never ignored, allowed or lowered)
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Someone between you and the source changed it, or the release was edited; don't install this copy.

Fires when

The bytes Sightread downloaded do not match the hash, checksums or signature the source publishes for this version, or the integrity your lockfile recorded. Something between you and the source changed them, or the release was edited after it was published. Nothing else in the report can be trusted for this copy.

False alarms

WordPress.org rebuilds readme.txt after a release; a difference in readme.txt only is noted, not flagged. A proxy that rewrites downloads causes this too and is worth fixing.

How to review

  1. Run the review again later and from another network to rule out a broken mirror or proxy.
  2. Compare the files listed in the finding with the copy on the registry's own site.
  3. Report the mismatch to the maintainers or the registry if it stays.

Incidents

None recorded yet.

version-republished

Same version, different bytes than the lockfile recorded

  • Severity: critical (never ignored, allowed or lowered)
  • Confidence: confirmed
  • Ecosystems: npm, composer
  • Advice: Keep the copy you have. The same version should never change its bytes.

Fires when

Your lockfile recorded one integrity hash or commit for this exact version. The registry now serves different bytes under the same version number. A published version should never change, so this points to a moved tag, a rewritten Composer reference or a registry problem.

False alarms

Very rare. A private registry that rebuilds tarballs can cause it; fix the registry instead of accepting the new bytes.

How to review

  1. Compare the integrity or reference in the lockfile with what the registry reports now.
  2. For Composer, look at the repository's tag history for a tag that moved.
  3. Keep the bytes you installed until the maintainers explain the change.

Incidents

None recorded yet.

approved-version-changed

Allowed version now has a different hash

  • Severity: critical (never ignored, allowed or lowered)
  • Confidence: confirmed
  • Ecosystems: all
  • Raised by: the allow list check, not a detector
  • Advice: Review it again. The allow entry covers other bytes than these.

Fires when

Your allow file records a reviewed archive for this version by its sha256. The archive the source serves now has a different hash. The review you recorded does not cover these bytes.

False alarms

WordPress.org rebuilds zips without changing the files. Allow entries for WordPress.org use the manifest hash, so a rebuild does not fire.

How to review

  1. Check who added the allow entry and when (git log on .sightread/allowed.yaml).
  2. Review the new archive as if it had never been allowed.
  3. Replace the entry only after that review.

Incidents

None recorded yet.

known-malicious

Listed as malicious by OSV

  • Severity: critical (never ignored, allowed or lowered)
  • Confidence: confirmed
  • Ecosystems: npm, composer
  • Advice: Don't install it. Rotate the credentials of any machine that already did.

Fires when

OSV lists a MAL- record for this version: a security team found code in it that was published to harm the people who install it. These records come from the OpenSSF malicious packages feed and from registry takedowns.

False alarms

A record that turns out wrong is withdrawn; the OSV page shows when that happened.

How to review

  1. Open the OSV record linked in the finding.
  2. Find every machine that already installed this version, CI runners included.
  3. Rotate the credentials those machines hold.
  4. Pin the last version before it.

Incidents

  • Shai-Hulud: Hundreds of republished npm packages were listed as MAL- records within days.

wporg-closed

Closed on WordPress.org

  • Severity: critical
  • Confidence: confirmed
  • Ecosystems: wp-plugin, wp-theme
  • Advice: Hold the update and find out why it was closed.

Fires when

WordPress.org closed this plugin or theme, so it gets no more updates from there. Closures follow security reports, guideline breaks or the author's own request.

False alarms

A closure at the author's request is high instead of critical: the author stopped, nothing was found.

How to review

  1. Read the closure reason in the finding and on the WordPress.org page.
  2. Look for a security advisory about the slug.
  3. Plan a replacement if it stays closed.

Incidents

  • Essential Plugin 2025: WordPress.org closed the portfolio's plugins once the changed code was found and pushed a cleaned release.

archive-path-escape

Archive entry points outside the package

  • Severity: critical (never ignored, allowed or lowered)
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Don't extract or install this archive.

Fires when

An entry name in the archive points outside the package folder through "..", an absolute path, a drive letter or a backslash, or is a name Windows opens as a device or stream (CON, NUL, a colon). Sightread never extracts it. An unzip tool or package manager might write it anywhere on disk.

False alarms

None known: release tools never write such names.

How to review

  1. List the archive's entries with a tool that only lists, never extracts.
  2. Compare with the archive of the previous release.
  3. Report it to the registry.

Incidents

None recorded yet.

remote-fetch-exec

Fetches remote code and runs it

  • Severity: critical
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Hold the update. Nothing local can tell you what the downloaded code will do.

Fires when

New code downloads something and runs it in the same function: the response reaches eval, include, a child process or a code file that is then loaded. What runs can change at any time without a new release. Reviewing this version tells you nothing about what it will run tomorrow.

False alarms

Fetching JSON and decoding it with json_decode does not fire. The fetch and the run must be in one function body.

How to review

  1. Open the finding with explain and read the hunk.
  2. Find the URL the code fetches and who runs that host.
  3. Check whether the changelog mentions it.
  4. Ask the maintainers before installing.

Incidents

  • intercom-php 2026: Version 5.0.2 downloaded Bun during composer install and ran an obfuscated script with it.
  • Gravity Forms 2025: The altered build fetched payloads from gravityapi.org and ran them.

decode-exec

Decodes hidden code and runs it

  • Severity: critical
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Hold the update. Decode the string offline and read it first.

Fires when

New code decodes a hidden string (base64, gzinflate, rot13, hex and the like) and runs the result. Readable code has no reason to hide from the people who read it. Built names such as 'ev'.'al' and comments between tokens are folded before matching.

False alarms

Decoding into output, a download or json_decode does not fire.

How to review

  1. Read the hunk with explain.
  2. Decode the string offline with a tool that only prints the result.
  3. Never run the decoded text to see what it does.

Incidents

  • event-stream 2018: flatmap-stream decrypted a payload at load time and ran it.
  • intercom-php 2026: The script it ran was packed and decoded before it ran.

admin-user-hidden

Hides users from the admin user list

  • Severity: critical
  • Confidence: confirmed
  • Ecosystems: wp-plugin, wp-theme
  • Advice: Hold the update. Check the users table itself for accounts the list hides.

Fires when

New code filters the admin user list (pre_user_query, users_list_table_query_args or views_users) to leave users out of it. An administrator nobody sees under Users keeps access after a cleanup.

False alarms

Filters that only add columns or sort the list do not fire.

How to review

  1. Read the filter callback in the hunk.
  2. Check which users it removes from the list.
  3. Compare the users table in the database with what the Users screen shows.

Incidents

None recorded yet.

lockfile-source-change

Lockfile entry moved off the registry

  • Severity: high
  • Confidence: confirmed
  • Ecosystems: npm, composer
  • Advice: Find out who moved this entry off the registry before merging.

Fires when

The lockfile entry for this package used to come from the registry. Now it resolves to a git URL, a local file, a tarball or another host. Packages from those places skip the registry's integrity and publication records.

False alarms

An entry that was already off the registry is listed under Not checked (vcs) and does not fire.

How to review

  1. Read the resolved URL in the lockfile diff.
  2. Check whether your team made the change on purpose.
  3. If nobody did, find the commit that changed it.

Incidents

None recorded yet.

archive-link

Archive holds a link or special file

  • Severity: high
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Check where the link points before installing.

Fires when

The archive holds a symbolic link, hard link, device or FIFO entry. Sightread records it and never follows it. A link whose target leaves the package can make an extractor read or write elsewhere; then it is critical.

False alarms

npm packages sometimes ship links that stay inside the package; those stay high.

How to review

  1. Read the link target in the finding.
  2. Check whether the previous release had the same link.
  3. Prefer an install method that refuses links.

Incidents

None recorded yet.

archive-collision

Archive holds two entries with the same name

  • Severity: high
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Don't trust either copy until the archive is rebuilt.

Fires when

The archive holds two entries with the same name, or names that differ only in case or Unicode normalisation. Different tools keep different copies. The file you review may not be the file that gets installed.

False alarms

None known: release tools do not write duplicate names.

How to review

  1. Find both entries named in the finding.
  2. Compare their contents.
  3. Ask the maintainers to rebuild the archive.

Incidents

None recorded yet.

unanalysed

Code that could not be analysed

  • Severity: high
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Read these files by hand. Sightread could not.

Fires when

Sightread could not read one or more code files: too large, a parse problem, a decode limit or the time budget. Hiding code behind such limits is a known trick. A gap is reported instead of a clean result.

False alarms

Binary and media files over the size cap are listed, not flagged, unless they hold code.

How to review

  1. Read the reason in the finding.
  2. Open the file and see what makes it unusual.
  3. Raise the limit with --max-size if it is a known large bundle.

Incidents

  • Shai-Hulud 2025: The second wave hid its payload in a 9.7 MB obfuscated file.

eval-dynamic

Runs code built at run time

  • Severity: high
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Hold the update until you know where the evaluated text comes from.

Fires when

New code runs text built at run time: eval, assert with a string, create_function, preg_replace with /e, new Function, vm, or a function whose name is built from strings or taken from request data. Such code does whatever its input says.

False alarms

eval in a comment or a string, assert() with a boolean and the webpack and esbuild runtime helpers (webpack_require, new Function("return this")) do not fire. Minifying a file does not hide a new eval.

How to review

  1. Read the hunk with explain.
  2. Find where the evaluated text or the function name comes from.
  3. If it comes from a request, a remote response or a decoded string, treat it as remote control of the site.

Incidents

  • Essential Plugin 2025: version_info_clean() called @$clean(...), a function name taken from fetched data.

obfuscation

Adds a large encoded or scrambled blob

  • Severity: high
  • Confidence: likely
  • Ecosystems: all
  • Advice: Hold the update. Nobody can review code that can't be read.

Fires when

The update adds a large encoded or scrambled blob (base64, hex, \x chains or char-code arrays with high entropy), or a code file far more random than the rest of the package. Code nobody can read can't be reviewed.

False alarms

Data URIs in CSS and SVG, base64 fonts, .pot and .mo files, source maps and recognised minified bundles do not fire.

How to review

  1. Read the file name and the blob's size in the finding.
  2. Decode it offline with a tool that only prints.
  3. Check whether the project publishes the readable source.

Incidents

  • event-stream 2018: flatmap-stream carried its payload as an encrypted blob.
  • intercom-php 2026: The stealer it ran was obfuscated.

input-unserialize

Unserializes request or remote data

  • Severity: high
  • Confidence: likely
  • Ecosystems: wp-plugin, wp-theme, composer
  • Advice: Hold the update. Unserializing outside data can run code through classes already loaded.

Fires when

New code passes request data or a remote response to unserialize or maybe_unserialize without allowed_classes => false. Unserializing outside data lets whoever controls it build objects from classes the site already loads. That is a well known way to run code on a PHP site.

False alarms

maybe_unserialize(get_option(...)) of the plugin's own option does not fire.

How to review

  1. Find the source of the data in the hunk.
  2. Check for allowed_classes => false.
  3. Ask the maintainers why json_decode would not do.

Incidents

  • Essential Plugin 2025: fetch_ver_info() passed a remote response to @unserialize().

write-outside-package

Writes files outside its own folder

  • Severity: high
  • Confidence: likely
  • Ecosystems: all
  • Advice: Hold the update. Find out what it writes there and why.

Fires when

New code writes files outside its own folder: under ABSPATH, wp-content, mu-plugins, other plugins, the home folder or a parent path. Writing wp-config.php, .htaccess, mu-plugins, .npmrc, .ssh, .git or CI files makes it critical. A package rarely needs more than its own folder, uploads and the temp folder.

False alarms

Writes under wp_upload_dir(), the package's own folder and the OS temp folder do not fire.

How to review

  1. Read the target in the finding.
  2. Check what is written and when.
  3. Look for the change in the changelog.

Incidents

  • Essential Plugin 2025: Eight months after the release the code wrote about 6 KB into wp-config.php.

admin-user-create

Creates an administrator account

  • Severity: high
  • Confidence: confirmed
  • Ecosystems: wp-plugin, wp-theme
  • Advice: Hold the update. Check the site's users for accounts you don't know.

Fires when

New code creates an administrator account, sets a user's role to administrator, grants super admin or writes wp_capabilities directly. A plugin almost never needs to make administrators on its own.

False alarms

Creating a user with a role other than administrator does not fire.

How to review

  1. Read the user name, email and role in the hunk.
  2. Check when the code runs: activation, a request or a cron job.
  3. Look at the site's users for accounts created recently.

Incidents

  • Social Warfare 2024: Injected code created the administrators Options and PluginAuth.
  • Gravity Forms 2025: The altered build added an administrator account.

capability-change

Grants admin capabilities to another role

  • Severity: high
  • Confidence: likely
  • Ecosystems: wp-plugin, wp-theme
  • Advice: Check which role gets admin rights and whether the changelog says why.

Fires when

New code grants admin-level capabilities (manage_options, edit_plugins, install_plugins, unfiltered_html, promote_users) to a role other than administrator. A new user_has_cap or map_meta_cap filter that grants them counts too. A subscriber with manage_options can take over the site.

False alarms

An install routine that adds the plugin's own custom capability to administrator does not fire.

How to review

  1. Read which role gets which capability.
  2. Check whether the plugin documents that role.
  3. Look at the roles on a staging copy after the update.

Incidents

None recorded yet.

update-suppression

Blocks updates of other plugins or themes

  • Severity: high
  • Confidence: likely
  • Ecosystems: wp-plugin, wp-theme
  • Advice: Hold the update. It stops other packages from updating.

Fires when

New code filters update checks (site_transient_update_plugins, auto_update_plugin and the like) so other plugins, themes or core stop getting updates. Blocked updates keep a site on versions with known problems. They also keep a changed copy from being replaced.

False alarms

A plugin that filters only its own update entry (its basename) does not fire.

How to review

  1. Read which packages the filter removes.
  2. Check whether it touches only its own entry.
  3. Compare the Updates screen on a staging copy.

Incidents

  • Gravity Forms 2025: The altered build blocked updates.

bot-cloaking

Shows search engines different content

  • Severity: high
  • Confidence: likely
  • Ecosystems: wp-plugin, wp-theme, npm
  • Advice: Hold the update. It shows search engines what you can't see.

Fires when

New code checks for search engine crawlers or logged-in visitors and prints fetched or encoded content in that branch. Owners and visitors see a normal page while search engines index something else.

False alarms

SEO plugins that only add meta tags for bots do not fire: the branch must print fetched or encoded content.

How to review

  1. Read the branch in the hunk.
  2. Fetch a page of a staging copy with a crawler user agent and compare.
  3. Look in your search console for pages you don't recognise.

Incidents

  • Display Widgets 2017: The new owner's release showed hidden content to visitors who were not logged in.
  • Essential Plugin 2025: Spam was shown to Googlebot only.

credential-access

Reads credentials or tokens

  • Severity: high
  • Confidence: likely
  • Ecosystems: all
  • Advice: Hold the update. Rotate tokens on any machine that already ran it.

Fires when

New code reads credential files (.npmrc, .ssh, .aws, auth.json, wp-config.php constants) or token variables (NPM_TOKEN, GITHUB_TOKEN, AWS_*, *_SECRET). A stolen token lets someone publish or deploy as you.

False alarms

Reading process.env.NODE_ENV and settings the package documents does not fire.

How to review

  1. Read what is read and where it goes next.
  2. Check whether the package documents needing it.
  3. If it runs at install time, treat CI tokens as exposed.

Incidents

  • Shai-Hulud: Install scripts read npm and GitHub tokens and published the owners' other packages with them.
  • phpass 2022: The hijacked release sent AWS keys from the environment.

data-exfil

Sends site data to a new host

  • Severity: high
  • Confidence: likely
  • Ecosystems: all
  • Advice: Hold the update until you know what is sent and to whom.

Fires when

New code sends site data (users, admin_email, options, environment variables or files) over the network from the function that reads it. The host is new in this version. Telemetry to a host the old version already used is medium.

False alarms

Telemetry to a host that is already in the old version is medium.

How to review

  1. Read what is collected and where it goes.
  2. Look for it in the privacy policy or the changelog.
  3. Block the host on a staging copy and see what changes.

Incidents

  • Social Warfare 2024: The new administrator accounts were sent to 94.156.79.8.
  • phpass 2022: AWS keys from the environment were sent to a remote host.

code-in-asset

Hides code in an image, font or text file

  • Severity: high
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Hold the update. Code doesn't belong in an image or a font.

Fires when

PHP or JS code sits in a file that should hold none (an image, a font, a text file) or after __halt_compiler(). Code hidden there escapes reviews that only read .php and .js files. It is critical when another file includes it.

False alarms

Real images whose binary data contains the bytes <? do not fire: a PHP open tag must be followed by a statement that parses.

How to review

  1. Open the file with a hex viewer, not an image viewer.
  2. Search the package for includes of its name.
  3. Compare it with the same file in the old version.

Incidents

None recorded yet.

npm-install-script-new

Adds or changes an install script

  • Severity: high
  • Confidence: confirmed
  • Ecosystems: npm
  • Advice: Install with --ignore-scripts until someone has read the script.

Fires when

The package adds or changes a preinstall, install, postinstall or prepare script, a binding.gyp that makes npm build on install, or a binding.gyp command node-gyp runs during that build. These scripts run on every machine that installs the package, CI included, before anyone reads the code. Sightread reads the script and the files it calls.

False alarms

An unchanged node-gyp install, a swap to another native build with plain flags only, a binding.gyp edit that runs no new command and a prepare that only runs husky or build tools the package already used do not fire. A build that downloads from a URL or builds from another directory does.

How to review

  1. Read the script in the finding and the files it runs.
  2. Install with --ignore-scripts until it is reviewed.
  3. Check that the repository has the same script.

Incidents

  • axios 2026: The new dependency plain-crypto-js dropped a remote access tool from its postinstall script.
  • Shai-Hulud: Every republished package carried a new install script.

composer-plugin-new

Runs code when Composer installs it

  • Severity: high
  • Confidence: confirmed
  • Ecosystems: composer
  • Advice: Keep it out of allow-plugins until the plugin code is reviewed.

Fires when

The package becomes a composer-plugin or adds event scripts (post-install-cmd, post-update-cmd, post-autoload-dump, pre-*). Composer runs that code on install and update on every machine, CI included.

False alarms

Scripts for the package's own workflow (test, lint) and scripts-descriptions do not fire.

How to review

  1. Read the plugin class or the scripts in composer.json.
  2. Check allow-plugins in your root composer.json.
  3. Install with --no-plugins --no-scripts until it is reviewed.

Incidents

  • intercom-php 2026: Version 5.0.2 became a Composer plugin on post-install-cmd and post-update-cmd.

owner-change

New owner or publisher

  • Severity: high
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Read this release closely. The people behind it changed.

Fires when

The people who publish this package changed: no maintainer of the old version remains, or the release came from someone who never published it before. Bought or hijacked packages often ship their first harmful release right after the handover. A change of hands is often normal; the trust you gave the old team simply does not carry over.

False alarms

A team that only grew does not fire. When WordPress.org takes a plugin over, the note says so.

How to review

  1. Read the old and new maintainers in the evidence.
  2. Look for an announcement of the handover.
  3. Review this release more closely than usual.

Incidents

  • Essential Plugin 2025: A buyer of 31 plugins shipped the changed code in the first release after the sale.
  • event-stream 2018: The author handed the package to a volunteer who added flatmap-stream.
  • Display Widgets 2017: Bought for $15,000; the next release added hidden content.

provenance-dropped

Release lost its build provenance

  • Severity: high
  • Confidence: confirmed
  • Ecosystems: npm
  • Advice: Hold the update until the maintainers confirm they published it.

Fires when

The installed version was published with provenance or trusted publishing, which ties it to a CI run in the public repository. This release has none. It may have been published from a laptop with a stolen token.

False alarms

Packages that never had provenance do not fire.

How to review

  1. Compare with the provenance of earlier releases on npmjs.com.
  2. Check the repository for a matching tag and CI run.
  3. Ask the maintainers whether the release process changed.

Incidents

  • axios 2026: 1.14.1 was published outside the project's trusted publishing.

typosquat

New dependency named like a popular package

  • Severity: high
  • Confidence: likely
  • Ecosystems: npm, composer
  • Advice: Check the dependency's name letter by letter.

Fires when

A new dependency is named like a popular package with a letter swapped, a separator changed, a scope confused, a look-alike character or a word like "plain" or "official" added. Such names count on nobody reading them closely.

False alarms

Names that are popular themselves (preact, react-dom, lodash.merge, colors next to color) do not fire, nor do names under the popular package's own npm scope or Composer vendor, which only its owner can publish to, nor packages that share a maintainer with the popular one.

How to review

  1. Compare the name with the popular one in the evidence.
  2. Check who publishes it and when it first appeared.
  3. Ask why the package needs it.

Incidents

  • axios 2026: plain-crypto-js, named like crypto-js, was published hours before axios added it.

known-vulnerable

Has a known vulnerability

  • Severity: high
  • Confidence: confirmed
  • Ecosystems: npm, composer
  • Advice: Pick a version that fixes the advisory if there is one.

Fires when

OSV lists an advisory for this version that it does not fix. Updating to it keeps or brings in a known problem.

False alarms

An advisory this version fixes is shown as info, titled "fixes ".

How to review

  1. Read the advisory.
  2. Check whether a later version fixes it.
  3. Decide whether your site uses the affected code.

Incidents

None recorded yet.

dependency-confusion

Private name also published publicly

  • Severity: critical
  • Confidence: confirmed
  • Ecosystems: npm, composer
  • Advice: Pin this name to your private registry.

Fires when

A private package name of yours also exists on the public registry with a higher version. A misconfigured install can take the public one, which anyone could have published.

False alarms

Checked only with --check-confusion.

How to review

  1. Check that your registry settings pin the scope or vendor to your private registry.
  2. Look at who published the public package.
  3. Claim the name publicly if you can.

Incidents

None recorded yet.

outbound-new-domain

Contacts a new domain

  • Severity: medium
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Find out who runs this host before updating.

Fires when

New code names a host the old version never did. Raw IP addresses, blockchain RPC endpoints, paste sites, URL shorteners, dynamic DNS and raw GitHub content are high. Sightread lists hosts and never contacts them.

False alarms

Hosts already in the old version, the package's own homepage and repository hosts (not shared code hosts such as github.com), your domains_ok and the shipped list of well-known hosts are info. So are URLs in readmes, comments and docs folders.

How to review

  1. Find out who runs the host.
  2. Look for it in the changelog.
  3. Read what the code sends there and what it does with the answer.

Incidents

  • Essential Plugin 2025: The code found its server through an Ethereum contract over public RPC hosts.
  • Social Warfare 2024: Data went to the raw IP address 94.156.79.8.
  • Gravity Forms 2025: The altered build called gravityapi.org.

rest-open-route

Adds a REST route anyone can call

  • Severity: medium
  • Confidence: likely
  • Ecosystems: wp-plugin, wp-theme
  • Advice: Check what this route does for someone who isn't logged in.

Fires when

The update adds a REST route whose permission_callback is __return_true or missing, so anyone on the internet can call it. It is high when the same file also gains code execution, unserialize, file writes or user changes.

False alarms

Read-only routes that return public data stay medium.

How to review

  1. Read the route's callback.
  2. Check what it returns or changes.
  3. Call it on a staging copy without logging in.

Incidents

  • Essential Plugin 2025: Version 2.6.7 added a REST route with permission_callback __return_true.

changelog-mismatch

Changelog does not mention what the code adds

  • Severity: medium
  • Confidence: likely
  • Ecosystems: all
  • Advice: Hold the update. Nothing in the changelog explains this.

Fires when

The changelog entry for this version is missing or generic ("compatibility", "tested up to", "bug fixes") while the code adds network, execution, file write, user or install-time behaviour. A release that adds such things without a word about them deserves a question.

False alarms

An entry that names the capability (the domain, telemetry, auto-update, license check) does not fire.

How to review

  1. Read the changelog entry next to the findings it does not explain.
  2. Look at the repository's commits for the release.
  3. Ask the maintainers.

Incidents

  • Essential Plugin 2025: The changed release said only "Check compatibility with WordPress version 6.8.2".

dependency-new

Adds a runtime dependency

  • Severity: medium
  • Confidence: confirmed
  • Ecosystems: npm, composer
  • Advice: Look at the new dependency as closely as at the package itself.

Fires when

The update adds a runtime dependency, or a lockfile change adds a package. Its code and everything it pulls in now run in your project. It is high when the dependency is younger than 7 days or has install scripts.

False alarms

devDependencies are info, as are development entries a lockfile adds.

How to review

  1. Check who publishes the dependency and how old it is.
  2. Read its install scripts.
  3. Ask whether the change needs it.

Incidents

  • event-stream 2018: A new maintainer added flatmap-stream.
  • axios 2026: 1.14.1 added plain-crypto-js, published hours earlier.

composer-autoload-files

Adds a file that runs on every request

  • Severity: medium
  • Confidence: confirmed
  • Ecosystems: composer
  • Advice: Read the file. It runs on every request.

Fires when

A new autoload.files entry runs on every request that loads vendor/autoload.php, whether anything uses the package or not. Its code also goes through the code signals.

False alarms

A helpers file with only function definitions is low.

How to review

  1. Read the file.
  2. Check that it only defines functions.
  3. Look for anything it does at load time.

Incidents

None recorded yet.

fresh-release

Released very recently

  • Severity: medium
  • Confidence: likely
  • Ecosystems: all
  • Advice: Wait a few days before installing it.

Fires when

The version was published less than min_age ago (72 hours by default). Most harmful releases are found and pulled within days, so waiting costs little.

False alarms

None: it is a quarantine hint.

How to review

  1. Wait until the release is older than min_age.
  2. Run the review again.
  3. Install sooner only when it fixes something you need now.

Incidents

  • Shai-Hulud: The waves were caught within days.
  • intercom-php 2026: Version 5.0.2 was live for 1 hour 44 minutes.

dormant-release

First release after a long silence

  • Severity: medium
  • Confidence: likely
  • Ecosystems: all
  • Advice: Check who is behind the first release in a year.

Fires when

The previous release came out more than a year before this one. A package that wakes up after a long silence often has a new owner or a hijacked account behind it.

False alarms

None: stable packages release rarely too. The signal only asks for a look.

How to review

  1. Check this update for owner and publisher signals.
  2. Look for an announcement.
  3. Read the whole diff.

Incidents

  • phpass 2022: An abandoned package got a new release from a hijacked account.

git-tag-missing

No matching tag in the repository

  • Severity: medium
  • Confidence: likely
  • Ecosystems: npm
  • Advice: Find the source this version was built from.

Fires when

The GitHub repository has no tag for this version (v, and @ were tried). A release with no matching source was likely published by hand, outside the project's usual process. It is checked only when the update also has owner-change, npm-install-script-new or provenance-dropped.

False alarms

Monorepo tag forms are tried before this fires.

How to review

  1. Look at the repository's tags and releases.
  2. Compare the package contents with the nearest tag.
  3. Ask the maintainers.

Incidents

  • axios 2026: 1.14.1 had no matching git tag.

size-jump

Code grew far more than usual

  • Severity: medium
  • Confidence: likely
  • Ecosystems: all
  • Advice: Find out what the new bulk is.

Fires when

The package's code grew far more than its history explains, or a minified or binary file appeared in a package that had none. Large payloads hide in large files.

False alarms

New languages/, vendor/ or dist/ folders lower it to info.

How to review

  1. Look at the largest added files.
  2. Check whether they are vendored libraries or build output.
  3. Compare with the repository.

Incidents

  • Display Widgets 2017: A release added a 38 MB GeoIP download.
  • Shai-Hulud 2025: The second wave shipped a 9.7 MB obfuscated file.

unicode-hidden

Hidden or bidirectional characters in code

  • Severity: medium
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Read these lines with hidden characters shown.

Fires when

New code holds bidirectional control or zero-width characters, or identifiers that mix scripts. They make code read differently from how it runs (Trojan Source).

False alarms

Translation files and i18n strings are info.

How to review

  1. Open the file in an editor that shows hidden characters.
  2. Read the lines in the finding.
  3. Ask for them to be removed from code.

Incidents

None recorded yet.

secret-added

Adds a credential to the code

  • Severity: medium
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Tell the maintainers. A published key must be rotated.

Fires when

An added line holds something shaped like a credential: an API key, a token or a private key. Shipping it exposes it to everyone who installs the package. Sightread shows it masked.

False alarms

Keys in tests and fixtures folders and documented examples (EXAMPLE, xxxx) are info.

How to review

  1. Check whether the key looks real.
  2. Tell the maintainers so they rotate it.
  3. Never use it yourself.

Incidents

None recorded yet.

cron-new-hook

Schedules a new background task

  • Severity: low
  • Confidence: confirmed
  • Ecosystems: wp-plugin, wp-theme
  • Advice: Check what the new background task does.

Fires when

The update schedules a new background task (wp_schedule_event, Action Scheduler or a new cron interval). Code in a scheduled task runs without anyone visiting a page. It is medium when the hook's file also gains network or code execution.

False alarms

A documented cleanup task stays low.

How to review

  1. Read what the scheduled callback does.
  2. Check how often it runs.
  3. Look for it in the changelog.

Incidents

None recorded yet.

license-change

License changed

  • Severity: low
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Check the new license before you ship it.

Fires when

The declared license changed. That can change what you may do with the code. A license change sometimes comes with a sale or a fork.

False alarms

None.

How to review

  1. Read the old and the new license.
  2. Check with whoever handles licensing for your projects.

Incidents

None recorded yet.

abandoned

Marked abandoned or not updated in years

  • Severity: low
  • Confidence: confirmed
  • Ecosystems: wp-plugin, wp-theme, composer
  • Advice: Plan a replacement.

Fires when

Packagist marks the package abandoned, or WordPress.org shows no update in two years. Abandoned packages get no security fixes. Their names and accounts also attract takeovers.

False alarms

None.

How to review

  1. Look for the suggested replacement.
  2. Plan the move.

Incidents

  • phpass 2022: An abandoned package was taken over through its maintainer's expired email domain.

local-drift

Installed copy differs from the official one

  • Severity: info
  • Confidence: confirmed
  • Ecosystems: wp-plugin, wp-theme, composer
  • Advice: Updating will overwrite these files. Keep a copy of edits you want.

Fires when

The installed copy differs from the official copy of the installed version. Someone may have edited files on the server, or something else changed them. Updating will overwrite them.

False alarms

Changes to readme.txt, .po, .mo and .pot files and JSON translations under languages/ are not counted. PHP translation files (.l10n.php) are: PHP runs them.

How to review

  1. Compare the files in the finding with the official copy.
  2. Decide whether the changes are edits you made.
  3. Keep a copy of edits you want before updating.

Incidents

None recorded yet.

php-requirement

PHP requirement changed

  • Severity: info
  • Confidence: confirmed
  • Ecosystems: wp-plugin, wp-theme, composer
  • Advice: Check your server's PHP version first.

Fires when

The minimum PHP version changed. A site on an older PHP may stop working after the update.

False alarms

None.

How to review

  1. Check your server's PHP version.
  2. Test the update on a staging copy.

Incidents

None recorded yet.

files-removed

Removes security-relevant files

  • Severity: info
  • Confidence: confirmed
  • Ecosystems: all
  • Advice: Check that the removed checks moved somewhere else.

Fires when

The update deletes files that held security checks: nonces, sanitisers, escaping, capability checks, .htaccess deny rules or index.php silence guards. Removing them can open paths that were closed.

False alarms

A refactor that moves the checks still lists the removed file; the info level reflects that.

How to review

  1. Check whether the checks moved to another file.
  2. Read the changelog.
  3. Test the affected screens.

Incidents

None recorded yet.


← Your first review · Home · Allow and baseline →

🏠 Home

Get started

rhythm in depth

Tuner

Tuner in depth

Soundcheck

Soundcheck in depth

Echo

Echo cookie consent

Echo in depth

Roadie

Roadie in depth

Sightread

Sightread in depth

Project

Clone this wiki locally