Privacy and network behaviour¶
The promise¶
No third party sees your dependency graph. Network use is exactly what
composer updateitself would do against the repositories you have configured.
This is deliberately narrower than "fully offline". Composer's solver needs package metadata for
every version it considers, and it fetches that from the repositories in your composer.json,
normally packagist.org. A remediation planner that validates with Composer's solver therefore
performs the same metadata requests Composer performs during an update. Those requests reveal
package names to the repository, as any composer update does.
What leaves the machine¶
| Data | Sent to | When |
|---|---|---|
| Nothing about your project | github.com (this project's advisory-db-latest release), or the mirror you configure |
Keeping the advisory database current, by default on every run: one small request for the publisher's latest.json or .sha256, and the database download itself when the copy is missing or stale. The request carries no package names; matching happens locally against the file. --no-database, --offline or a local path as the source stop it. |
| Package names in your lock file | Advisory source (the configured repositories, Packagist by default) | Only when no advisory database can be had (or with --no-database): the same request composer audit makes, a POST of up to 500 names per batch. Versions are not sent; matching happens locally. Should Composer's in-process advisory API be unusable, the same lookup runs as a composer audit --locked --no-plugins --no-scripts subprocess against the same repositories, with the same request and without activating the project's plugins. |
| Package names being resolved | Your configured Composer repositories | Solver validation, identical to composer update. Served from the Composer cache when possible. |
| Nothing else | No telemetry, no accounts, no project files. |
Private repositories configured in composer.json or auth.json are used exactly as Composer
uses them, because the plugin runs inside your Composer.
The plugin boundary¶
composer remediate is a plugin command. Composer activates every plugin the analysed project
allows while it discovers plugin commands, so by the time remediate runs those plugins have
already executed, exactly as they do for composer install or composer show. The planner itself
then builds fresh Composer instances with plugins and scripts disabled for every candidate solve and
never writes to the project. For a project you maintain, that is the expected behaviour of any
Composer command. For a project you do not trust, use the composer-remediate binary installed
alongside the plugin: it boots Composer with --no-plugins --no-scripts forced from the first
instruction, so nothing from the analysed project runs, and it applies --offline before any HTTP
client exists.
What "nothing from the analysed project runs" rests on: the binary never includes a project's
vendor/autoload.php (Composer's autoloader executes every autoload.files entry, which is project
code). It loads Composer's classes from the Composer phar it finds (REMEDIATE_COMPOSER_BINARY, or
composer on PATH) and the plugin's own classes through a plain PSR-4 mapping. That holds for a
project-local installation (vendor/bin/composer-remediate next to the project's autoloader) as much
as for a global one. Non-phar Composer installations are accepted only when named explicitly through
REMEDIATE_COMPOSER_BINARY, because their bootstrap includes that installation's own autoloader.
The candidate solves, in both entry points, run in scratch copies; the subprocess route pins
COMPOSER to the scratch manifest so an inherited COMPOSER=alternate.json cannot redirect an
update to the analysed project's lock file.
--offline¶
With --offline the planner sets Composer's COMPOSER_DISABLE_NETWORK before it builds its own
Composer instance (with composer remediate) or before Composer boots at all (with
composer-remediate).
Composer then answers cached metadata as "not modified" and fails any request that is not in the
cache. If a solve needs metadata that is missing, the planner stops with a clear error instead of
falling back to the network.
With an advisory database (--database-location) the first row of the
table disappears: advisories are read from a local SQLite file you built yourself or downloaded
once, and no package names are sent anywhere for the advisory lookup. Only https:// database
URLs are accepted, so a composer.json cannot point the tool at an internal plain-http service, and
credentials embedded in a URL never appear in messages or cache metadata. A database URL is fetched
along with its .sha256 sidecar; the download is verified against it, the verification outcome is
recorded next to the cached copy and repeated as a warning on every later run that reuses an
unverified copy, and a refresh that fails falls back to the cached copy with a warning in the report
naming its age.