Trail installs npm packages in a disposable sandbox and records every file they touch, every process they start and every connection they open, then attributes each action to the code that caused it.
Each package on the feed is installed inside a fresh, throwaway container. The install runs with scripts enabled while the system calls it makes are recorded. The package is then loaded the way a consumer would load it, and recorded again.
Every recorded action is tied back to the process that produced it, so the
package's own behaviour is separated from the ordinary noise of an
npm install. npm reads your .npmrc on every
install; that is npm doing its job, not a finding.
Trail is a triage tool rather than a classifier. Its job is to surface behaviour worth a human's attention, not to pass final judgment.
The package is installed and imported inside an isolated container while its system calls are traced. Files read and written, processes spawned, permissions changed, sockets opened.
Events are correlated as well as counted. A file made executable and then run, or a secret read and then compressed into an archive named after it, means something that neither event means alone.
This engine sets the verdict.
The published archive is examined without being run. Rule matches against the source, the capabilities the code holds, and the package metadata as the registry reports it.
Every file also gets a SHA 256 and an entropy score, which reveals packed, compressed or encrypted content sitting inside an otherwise readable package.
This engine adds context to a verdict it does not set.
polymarket-stake-mathss@3.5.2 is two kilobytes of readable
JavaScript with one postinstall hook. This is what the sandbox
watched it do.
It opened credential file after credential file, compressed each one into a temporary directory, named every archive after whatever it had just taken, then opened outbound connections. The whole sequence took ten and a half seconds.
Read those rows one at a time and each is unremarkable. Packages read files.
Build tools run gzip. Installers write to temporary directories.
What makes this a finding is the order the actions happened in, which is
visible only while the code is running.
Nothing was taken. The container holds nothing real and is destroyed moments later, so what the package collected was a set of files that exist nowhere else.
The feed has already surfaced live threats, among them dependency confusion probes beaconing to out of band callback services, and a package encoding host details inside DNS queries.
Behaviour a human should look at. A lead, not a conviction.
Nothing notable observed. Behaviour outside the observation window is out of scope.
Two verdicts, deliberately. A third tier invites arguments about which shade something is instead of whether to look at it. Where a run was cut short, the result is REVIEW, because no coverage is not the same as nothing found.
If the registry pulls a package while it is being analysed, it drops off the
feed entirely. Security holdings are the exception: npm replaces the real
version with a 0.0.1-security placeholder, so there is nothing
left to install and nothing to observe. Those stay, filed under review and
marked as placeholders.
A sandbox is table stakes. The open problem is detection strategy, worked on across two branches in parallel.
Sandbox realism, against payloads that go quiet the moment they sense they are being watched.
Latest stable release of the sandbox is used for this feed currently. We plan to, eventually, also be able to run heuristics on the dropped binaries and make them available for download. They will come in the subsequent version of this sandbox.
A disputed verdict, a package worth a look, or just to talk. Cheers.
contact@snappyfeet.org