Verdict
Review
fixture:netbenign (version 1.0.0)
- Receipt
- d5af19b228
- Started
- 2026-11-04T15:50:14.232Z
- Finished
- 2026-11-04T15:50:32.198Z
- 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 |
|---|---|---|---|
| f | review | network attempt (DNS, TCP or UDP, TLS included; TLS bodies are not inspected), or a bind/listen in the sandbox network namespace | o1, 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 | downloads.qp-fixture.example |
| filesOutsidePackage | false | false | match | |
| 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 | downloads.qp-fixture.example | ||||
| tcp | downloads.qp-fixture.example | 80 | GET /prebuilt/v1/linux-x64.json | 0 |
Model's expectation before the run model, informational
The entry module only exports a static object, so requiring it performs no I/O. However, the package declares an `install` script (node check.js) described as probing for a binary download via a GET, which implies outbound network access and a spawned node child process at install time. No mechanism is indicated for writing files outside the package folder.
Model status: ok
Observations 5
| Id | Kind | Actor | Target | Blocked |
|---|---|---|---|---|
| o1 | net-dns | package | downloads.qp-fixture.example | no |
| o2 | net-tcp | package | downloads.qp-fixture.example | no |
| o3 | spawn | package | ||
| o4 | spawn | package | ||
| o5 | 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.