Commit Graph

15 Commits

Author SHA1 Message Date
Dylan Conway
703741b618 test(integration): use async spawn so the in-process proxy can respond; bump bun to 1.3.13 (#243)
* test(integration): use async spawn so the in-process proxy can respond

The integration tests run sandboxed curl commands against the HTTP/SOCKS
proxy that lives in the same process. Driving them with spawnSync blocks
this event loop for the duration of the wrapped command, so the proxy
request handler cannot run — curl is waiting on a server whose JS thread
is parked inside the very spawnSync that is waiting for curl. The suite
only passed because some bun versions happen to let the loop tick during
spawnSync; on linux/x86-64 that has been intermittent (the recent CI
flakes), and on newer bun it stops entirely.

Replace spawnSync with a small async spawnAsync helper that mirrors the
{stdout, stderr, status, signal} return shape. The event loop stays
alive, the proxy responds, and reset() between describes no longer hits
its 5s fallback.

Also bump CI bun to 1.3.13.

* test: move spawnAsync to shared helper, close stdin, convert remaining proxy tests

- Extract spawnAsync into test/helpers/spawn.ts so configurable-proxy-ports
  and update-config tests can share it (both drive curl through an
  in-process proxy and hit the same deadlock on bun >=1.3.2).
- Close child stdin so the child sees EOF immediately, matching spawnSync
  with no `input`. Without this, `su` in the privilege-escalation test
  waits on the open pipe.
- Convert the remaining proxy-hitting spawnSync call sites in those two
  files.
2026-05-05 13:14:19 -07:00
Dylan Conway
7f650392ee Bake BPF filter into apply-seccomp, build in CI (#199)
* Bake BPF filter into apply-seccomp, build in CI

The unix-block BPF filter is now generated as a C header at build time
and compiled directly into apply-seccomp. The separate .bpf file is gone,
as is the TS machinery that found, loaded, and tracked it.

vendor/seccomp/build.ts compiles the BPF generator, runs it for both
x64 and arm64, writes the bytes into unix-block-bpf.h, then compiles
apply-seccomp with that header #included. An #if defined(__x86_64__) /
#elif defined(__aarch64__) block in the header picks the right filter
at compile time.

The built binaries are no longer committed. release.yml runs a matrix
job on both an x64 and an arm64 runner, each building apply-seccomp
for its own architecture, uploading the result as an artifact. The
publish job downloads both into vendor/seccomp/{x64,arm64}/ before
npm publish, keeping the tarball layout unchanged.

* Build seccomp binaries in docker-tests CI job

* Remove stale references to on-disk BPF filter file

The two fail-closed tests in pid-namespace-isolation now test execve
failure instead of filter-file validation, since apply-seccomp no longer
takes a filter argument. README still described .bpf files in
vendor/seccomp/.

* Bump version to 0.0.47
2026-04-02 10:58:33 -07:00
Dylan Conway
e94c5fd01d Run full test suite in CI and migrate platform skips to describe.if (#197)
* Run full test suite in CI and migrate platform skips to describe.if

CI was running test:unit + test:integration, a curated subset of 5 files.
Most test files were never run in CI. Switch to `npm test` which runs
everything. Drop the test:unit/test:integration scripts.

Migrate the inline `if (skipIfNotLinux()) return` pattern to bun's native
`describe.if()`/`it.if()`. The old pattern made wrong-platform tests show
as pass (zero assertions, green checkmark) instead of skip — CI's test
count looked the same regardless of what actually ran. New
test/helpers/platform.ts exports isLinux/isMacOS/isSupportedPlatform.

Delete ~310 lines of unreachable tests from seccomp-filter.test.ts:
- skipIfNotAnt() gate checked USER_TYPE env var that nothing sets
- Two tests called wrapCommandWithSandboxLinux() with no restrictions,
  which returns the command unwrapped at the early-return check —
  expect("echo test").not.toContain("apply-seccomp") was vacuously true

Pin allow-read root-deny tests to /bin/bash — EXEC_DEPS doesn't list
/opt/homebrew, so execvp failed on Macs with homebrew bash as SHELL.

Add docker-tests CI job: unprivileged container on both arches,
exercises enableWeakerNestedSandbox end-to-end.

Drop push trigger from '**' to 'main' — PRs were running the full
matrix twice (once for branch push, once for the PR event).

* Replace mock.module with spyOn in linux-dependency-error tests

mock.module patches bun's module cache globally and never unmocks.
With npm test running all files in one process (instead of the old
test:unit + test:integration split), the mock leaked: every file that
imported getApplySeccompBinaryPath after this one got () => null, so
pid-namespace-isolation.test.ts and integration.test.ts failed in
beforeAll.

spyOn swaps one export binding; mockRestore in afterEach puts it back.
The callee's own import binding routes through the same slot in bun, so
checkLinuxDependencies sees the spy without any module-level surgery.

Also spies on whichSync directly rather than overwriting Bun.which on
globalThis — same fix, closer to what's actually being tested.

Drop stale README reference to the deleted test:integration script.

* Replace docker test-suite job with srt end-to-end test

The full suite assumes bwrap --proc /proc works; an unprivileged
container doesn't have CAP_SYS_ADMIN for that. Only tests that set
enableWeakerNestedSandbox can pass there.

Instead of filtering which unit tests to run, test the thing the job
is for: build srt, run it with enableWeakerNestedSandbox, check that
allowed writes land, denied writes don't, and the seccomp filter blocks
AF_UNIX. Gated on SRT_E2E_DOCKER so host jobs skip it.

* Rename docker job to match other Tests jobs

* Add required network key to docker test config

SandboxRuntimeConfigSchema requires network (no .optional()). Without it
loadConfig returns null, srt falls through to getDefaultConfig, and the
sandbox enforces a different allowWrite than the test expects.

* Add explicit timeouts to update-config sandboxed-curl tests

The three it.if(isLinux) tests each run two spawnSync calls with curl
--max-time 3 then --max-time 5. When example.com responds slowly both
curls run to their limits and the body takes ~8s, but bun's default
test timeout is 5000ms. bun aborts mid-body; afterEach runs reset()
against an in-flight spawn and the next test sees stale state.

These were never in test:integration so they never ran on CI before
this branch. On fast responses they complete in under 200ms.
2026-03-31 15:36:39 -07:00
Dylan Conway
ed5a909983 Fix enableWeakerNestedSandbox after apply-seccomp namespace changes (#196)
* Fix enableWeakerNestedSandbox after apply-seccomp namespace changes

apply-seccomp now creates a nested userns and writes /proc/self/setgroups
and uid_map before applying the seccomp filter. That broke
enableWeakerNestedSandbox in two ways:

  1. Without --proc, bwrap's --ro-bind / / leaves /proc read-only.
     apply-seccomp's setgroups write dies with EROFS.

  2. In unprivileged Docker (the flag's target), apply-seccomp's proc
     remount fails the kernel domination check — Docker's /proc masks
     are MNT_LOCKED in the less-privileged nested userns.

And the reason bwrap never got that far in Docker: bwrap only auto-adds
--unshare-user when EUID != 0. Docker's default is EUID=0 without
CAP_SYS_ADMIN; bwrap assumes it has caps, tries direct clone(NEWPID),
and EPERMs before apply-seccomp runs.

Changes:

  - bwrap args for weak mode: --unshare-user (force userns even as
    EUID=0) and --bind /proc /proc (restore rw /proc for setgroups)
  - apply-seccomp: tolerate mount(/proc) EPERM. The nested userns is
    the isolation boundary; the proc remount only hides outer PIDs
    from `ls /proc`.

Fixes the two failing mandatory-deny-paths tests that exercise
enableWeakerNestedSandbox. No test changes required.

Bump version to 0.0.46.

* Run mandatory-deny-paths tests in CI

These exercise enableWeakerNestedSandbox — the two tests that broke
when apply-seccomp started nesting namespaces. Add explicitly until
the full-suite CI change lands.

* Remove CI step comment
2026-03-31 12:56:06 -07:00
David Dworken
7ee4ac602d Isolate seccomp workload in nested PID ns and block io_uring (#183)
* Isolate seccomp workload in nested PID namespace and block io_uring

apply-seccomp now creates a nested user+PID+mount namespace before applying
the seccomp filter. The user command runs as PID 2 under a non-dumpable PID 1
reaper, with /proc remounted so only the inner process tree is visible. This
prevents the sandboxed command from ptracing or patching the unfiltered bwrap
init, bash wrapper, or socat helpers via /proc/N/mem, regardless of the host's
kernel.yama.ptrace_scope setting. Namespace setup failure aborts rather than
silently degrading.

The BPF filter now also blocks io_uring_setup/enter/register. IORING_OP_SOCKET
(Linux 5.19+) creates sockets without going through socket(), and seccomp
cannot inspect SQEs in the shared ring, so denying ring creation entirely is
the only safe option.

The filter generator now accepts an optional target-arch argument so a single
builder can emit both x64 and arm64 filters. Prebuilt binaries and filters are
regenerated for both architectures.

* Pass CAP_SYS_ADMIN to apply-seccomp and clear ambient caps before exec

apply-seccomp needs CAP_SYS_ADMIN to unshare PID+mount namespaces. The
original approach obtained it via unshare(CLONE_NEWUSER), but on hosts
where an LSM restricts unprivileged user namespaces (Ubuntu 24.04 with
AppArmor defaults), the nested userns is created without capabilities
and the setgroups write fails.

bwrap now passes --cap-add CAP_SYS_ADMIN (scoped to its user namespace)
so apply-seccomp can unshare directly. The nested-userns path remains as
a fallback for standalone invocation.

apply-seccomp clears the ambient capability set after remounting /proc,
so the sandboxed command's execve drops to zero capabilities and cannot
umount /proc to reveal the outer mount underneath. Two new tests cover
CapEff=0 and umount denial.

* chore: bump version to 0.0.44

* Add --unshare-user so --cap-add works with setuid bwrap

Setuid bwrap rejects --cap-add from non-root because it would grant
real host capabilities. --unshare-user forces user-namespace mode so
the capability is scoped to that namespace and the flag is accepted.

* Disable AppArmor userns restriction in CI instead of using setuid bwrap

Setuid bwrap rejects --cap-add from non-root, so that path is a dead end.
Instead, disable kernel.apparmor_restrict_unprivileged_userns in CI so
apply-seccomp's nested-userns path works without any bwrap cooperation.
This matches what production Ubuntu 24.04 users need to do anyway, now
documented in the README.

* Exit inner init as soon as the worker exits

reap_until was waiting for all children including orphaned background
processes reparented to PID 1, which hung the sandbox when the user
command backgrounded something long-running and then exited. Return
immediately when the worker terminates; PID 1 exiting tears down the
namespace and SIGKILLs any stragglers.
2026-03-30 17:07:40 -07:00
Dylan Conway
ef4afdef4d ci: add npm release workflow (#143) 2026-02-19 14:48:18 -08:00
Sosuke Suzuki
7c9dbfa8dc ci: add Node.js fallback test for whichSync 2026-02-04 07:58:55 +00:00
David Dworken
04fc873246 Update macOS CI runner from retired macos-13 to macos-15-large
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 18:16:39 +00:00
ollie-anthropic
380da4a976 fix integration tets 2025-11-13 02:10:31 -08:00
David Dworken
e88bbb7fd7 Add tests for binShell parameter with zsh and install zsh in CI
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 17:34:32 -07:00
David Dworken
bfab892a2e Rename workflow from "Integration Tests" to "Tests"
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 16:04:00 -07:00
David Dworken
a918f1dd6d Add unit tests to CI workflow and fix bridge process cleanup
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 16:02:32 -07:00
David Dworken
abd65b17f8 Remove Artifactory npm configuration from integration tests 2025-10-24 15:34:15 -07:00
David Dworken
4eb7b42b10 Enable ARM64 Linux runners in integration tests 2025-10-24 15:32:43 -07:00
David Dworken
23e9e22622 Add BPF/seccomp integration for Linux unix socket blocking and comprehensive testing infrastructure
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 13:55:33 -07:00