Verdict
Do not install
fixture:hostile (version 1.0.0)
- Receipt
- 8b42cfb93e
- Started
- 2026-11-04T15:43:06.477Z
- Finished
- 2026-11-04T15:43:24.352Z
- Source
- repo fixture, never published
Computed by fixed rules from what was observed. The model's explanation never changes it.
Rules that fired
| Rule | Tier | Why | Observations |
|---|---|---|---|
| a | do-not-install | write attempt (successful or blocked) outside /work, /tmp and HOME | o1 |
| b | do-not-install | access attempt to docker.sock or a credential path | o3 |
| d | do-not-install | network attempt carrying a payload (plaintext HTTP body/POST/PUT/PATCH/chunked, or raw bytes on non-TLS TCP) | o5 |
| f | review | network attempt (DNS, TCP or UDP, TLS included; TLS bodies are not inspected), or a bind/listen in the sandbox network namespace | o4, o5 |
| g | review | write inside HOME | o2 |
Prediction vs observed
Before the run, the model on Vultr Serverless Inference read the package and predicted its behaviour. The sandbox then recorded what happened.
3 match
| Dimension | Predicted | Observed | Result | Details |
|---|---|---|---|---|
| network | true | true | match | recolector.example.com |
| filesOutsidePackage | true | true | match | /etc/qp-fixture-marker, /home/qp/.qp-fixture-marker |
| childProcesses | true | true | match | /usr/bin/sh, /usr/local/bin/node |
Sinkhole log
Network attempts. Nothing left the sandbox.
| Type | Destination | Port | SNI | Method / path | Payload bytes |
|---|---|---|---|---|---|
| dns | recolector.example.com | ||||
| tcp | recolector.example.com | 80 | POST /collect | 15 |
Model's expectation before the run model, informational
The package declares an install script (node attempts.js) whose name and the author's own description ('makes benign attempts that look hostile') indicate it tries network access, out-of-tree writes, and subprocess spawns at install time. The entry module itself is inert, but installing/using the package in a sandbox will execute that script. Hosts and paths are best-guess placeholders since attempts.js contents were not provided.
Model status: ok
Observations 9
| Id | Kind | Actor | Target | Blocked |
|---|---|---|---|---|
| o1 | fs-write | package | /etc/qp-fixture-marker | yes |
| o2 | fs-write | package | /home/qp/.qp-fixture-marker | no |
| o3 | fs-access | package | /var/run/docker.sock | yes |
| o4 | net-dns | package | recolector.example.com | no |
| o5 | net-tcp | package | recolector.example.com | no |
| o6 | env-enumerate | package | ||
| o7 | spawn | package | ||
| o8 | spawn | package | ||
| o9 | load | harness |
Sandbox destroyed
yes: no container, network or volume of this task remains
Limits applied
{
"downloadSeconds": 120,
"downloadMaxBytes": 52428800,
"downloadMaxPackages": 300,
"sandboxSeconds": 60,
"pids": 64,
"memoryBytes": 268435456,
"cpus": 0.5,
"tmpfsWorkBytes": 134217728,
"tmpfsTmpBytes": 67108864,
"tmpfsHomeBytes": 16777216
}Known limitations
- L-4: ICMP is out of scope: the sandbox has no capability to open raw sockets, and unprivileged ICMP is not logged.
- L-4: TLS bodies are not inspected: the sinkhole records the SNI from the ClientHello and refuses the handshake.
- UDP other than DNS is logged as an attempt; its original destination comes from the kernel trace when the socket names it.
- DNS over TCP is logged as a TCP attempt to port 53 (query name not parsed).
- Env-var reads are seen by the in-process preload only; a package can bypass or forge it in its own process. Forged preload records can only add observations; trace-derived completeness markers come from the tracer stream.
- Inside the harness process every rule-relevant syscall is attributed to the package (the harness itself performs none).
- Paths written through fchdir-relative or unknown directory fds are reported as unresolved and treated as outside the allowed areas.
- Side channels inside the kernel (timing, /proc enumeration) are not observed.
- L-4: Evasion: in the shared pid namespace a package can see that it is traced (TracerPid, the strace process) and behave benignly; S1 does not hide the tracer.
- npm CLI processes do not get the env-read preload (npm fails with a wrapped process.env); an npm started by package code is therefore reported as incomplete instrumentation (review).
- L-1: Shell expansion of env variables (e.g. $NPM_TOKEN inside a lifecycle sh) and env reads by sh itself (perl and awk are removed from the image since S2, X10) are invisible to both the kernel trace and the preload (DEC-11r5; their file, network and process activity is still traced and judged).
- L-2: DEC-11r4: a package lifecycle that executes a script directly from /work, /tmp or HOME (e.g. "./install.sh" rather than "sh ./install.sh" or "node x.js") fails the instrumentation marker and gets review (i): the trace cannot tell a script from an ELF binary there (fail closed; narrowed in S2, X8).
- L-3: Download phase: a non-registry spec in the root manifest aborts before any network request; one deeper in the tree is rejected before it is fetched, but registry metadata of the dependencies visited before it has been fetched. Download-container egress is not limited to the registry host in S1.
- L-4: execveat is handled (envp never trusted, SR-3) but is covered by unit tests only, not by a runtime fixture.
- L-4: image builds are not byte-reproducible: the bases are pinned by digest, but distribution packages (strace, iptables, tcpdump) are installed at build time.
- L-6b: relative file operations of a child process printed before the clone return of its parent have no inherited cwd: they are unresolved, so a write gives (a) and a read gives review (fail closed).
- L-8: a chdir by a thread not yet attributed to any process that overlaps an unrelated fork marks that fork raced: relative paths of the child are unresolved (over-condemnation, never install).
- L-8b: a benign multi-threaded package whose non-forking thread ever changes directory (e.g. main thread process.chdir while a worker spawns) gets the relative paths of its children unresolved: relative writes give do-not-install (a), relative reads give review. Fail closed by design (dispatch 3).
- L-9: a chdir whose outcome is unknown (killed mid-call, "= ?", or never resumed before the trace ends) is treated as applied: the cwd of the thread group and of overlapping or later children become unresolved (fail closed).
- L-10: a process cloned with CLONE_FS but not CLONE_THREAD shares its working directory with its parent; both sides get an unresolved cwd for the whole trace (fail closed). Only native code issues such a clone; node, dash and perl do not.
- L-11: io_uring is not traced. Node 22 / libuv in the sandbox never issues io_uring_setup (checked with UV_USE_IO_URING=1), and native io_uring is denied by the default Docker seccomp profile the sandbox runs under; native code from a writable mount gives review anyway (L-2).
- L-13: a native addon (.node) that the package loads can read env in-process with getenv(); there is no exec and no preload record, so only the intent signal (c1) is lost. Same class as L-1: the sandbox env holds no secret value (C7) and any exfiltration still fires (d)/(f).
- Sinkhole memory bound: when more than 128 MB is buffered across open connections, the connection holding the most is recorded early with what it sent so far; later bytes on it are not counted.
- UDP payloads are logged but, per DEC-2r1, UDP attempts are rated review (f), never do-not-install by payload.