🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@mochi.js/core

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mochi.js/core - npm Package Compare versions

Comparing version
0.9.4
to
0.9.5
+1
-1
package.json
{
"name": "@mochi.js/core",
"version": "0.9.4",
"version": "0.9.5",
"description": "The library for faithful browser automation. Bun-native; relational fingerprint matrix, biomechanical input, stock Chromium-for-Testing.",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -383,2 +383,17 @@ /**

});
it("emits the AppArmor user-namespace hint for the canonical 'No usable sandbox' message (issue #52)", () => {
// The exact stderr emitted by Chromium on Kubuntu 25.10 — captured
// verbatim in the issue report.
const tail =
"[759176:759176:0511/132845.681682:FATAL:content/browser/zygote_host/" +
"zygote_host_impl_linux.cc:128] No usable sandbox! If you are running on " +
"Ubuntu 23.10+ or another Linux distro that has disabled unprivileged user " +
"namespaces with AppArmor, see ...";
const hint = diagnoseEarlyExitTail(tail);
expect(hint).toContain("user-namespace sandbox cannot initialize");
expect(hint).toContain("apparmor_restrict_unprivileged_userns");
expect(hint).toContain("Install an AppArmor profile");
expect(hint).toContain("--no-sandbox");
});
});

@@ -10,3 +10,3 @@ /**

import { mkdtemp, rm } from "node:fs/promises";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";

@@ -255,30 +255,61 @@ import { join } from "node:path";

// Linux + uid 0 (root) + no `--no-sandbox` anywhere → Chromium will refuse
// to start with the user-namespace sandbox. We auto-inject `--no-sandbox`
// (with a one-line warning naming the fingerprint trade-off) instead of
// letting `spawnChromium` crash with `EPIPE`. Users who explicitly want
// the sandbox under root can either run as a non-root user, `chmod 4755`
// the chrome-sandbox SUID helper, or pass their own `--no-sandbox` (which
// we'd see in args and skip this branch).
// Auto-fallback: --no-sandbox when Chromium's user-namespace sandbox
// CANNOT initialize. Two host configurations trigger this on Linux:
//
// 1. Running as root (uid 0). Chromium refuses to start under root with
// the user-namespace sandbox enabled. Hits CI containers, Docker
// defaults, etc.
// 2. Unprivileged user namespaces blocked by the kernel/AppArmor. This
// is the default on Ubuntu 23.10+ and Kubuntu 25.10+ (per the
// `apparmor_restrict_unprivileged_userns` knob) — Chromium emits
// `FATAL: No usable sandbox!` and exits. Issue #52. ANY non-root
// user on those distros hits this without intervention.
//
// In either case Chromium dies in the first few hundred ms with no CDP
// pipe ever opened. We inject `--no-sandbox` (with a one-line warning
// naming the fingerprint trade-off) instead of letting `spawnChromium`
// crash with `EPIPE`. Users who explicitly want the sandbox can:
// - run as a non-root user on a distro that allows unprivileged userns,
// - `chmod 4755` the `chrome-sandbox` SUID helper next to the CfT binary,
// - on Ubuntu 23.10+, install an AppArmor profile for the Chromium binary
// (see https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md),
// - or pass their own `--no-sandbox` in `args` (which we'd see and skip
// this branch).
//
// We DO NOT add `--no-sandbox` to DEFAULT_CHROMIUM_FLAGS (PLAN.md §8.6
// explicitly omits it as a fingerprint leak). This is a runtime fallback,
// not a default — only fires under the specific environment that would
// otherwise crash. The fingerprint-leak risk is documented in
// docs/quickstart.md "Linux gotcha — Chromium and root".
// otherwise crash.
if (
process.platform === "linux" &&
process.getuid?.() === 0 &&
!args.some((a) => a === "--no-sandbox" || a.startsWith("--no-sandbox=")) &&
!cfg.allowRootWithSandbox
) {
console.warn(
"[mochi] Detected root + Linux + missing --no-sandbox. " +
"Auto-adding --no-sandbox so Chromium can launch. " +
"This is a fingerprint leak per PLAN.md §8.6 — run as non-root or " +
"use the chrome-sandbox SUID helper for stealth-critical workloads. " +
"See docs/quickstart.md 'Linux gotcha — Chromium and root'. " +
"Pass `allowRootWithSandbox: true` to mochi.launch() to opt out of this fallback.",
);
args.push("--no-sandbox");
const isRoot = process.getuid?.() === 0;
const usernsBlocked = !isRoot && (await apparmorRestrictsUserns());
if (isRoot) {
console.warn(
"[mochi] Detected root + Linux + missing --no-sandbox. " +
"Auto-adding --no-sandbox so Chromium can launch. " +
"This is a fingerprint leak per PLAN.md §8.6 — run as non-root or " +
"use the chrome-sandbox SUID helper for stealth-critical workloads. " +
"See docs/getting-started/linux-server.md 'Linux gotcha — Chromium and root'. " +
"Pass `allowRootWithSandbox: true` to mochi.launch() to opt out of this fallback.",
);
args.push("--no-sandbox");
} else if (usernsBlocked) {
console.warn(
"[mochi] Detected AppArmor unprivileged user-namespace restriction " +
"(/proc/sys/kernel/apparmor_restrict_unprivileged_userns=1). " +
"Chromium's user-namespace sandbox cannot initialize on this host " +
"(Ubuntu 23.10+ / Kubuntu 25.10+ default). " +
"Auto-adding --no-sandbox so Chromium can launch. " +
"This is a fingerprint leak per PLAN.md §8.6 — for stealth-critical " +
"workloads, install an AppArmor profile for the Chromium binary " +
"(https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md) " +
"or run on a distro without the restriction. " +
"Pass `allowRootWithSandbox: true` to mochi.launch() to opt out of this fallback.",
);
args.push("--no-sandbox");
}
}

@@ -507,2 +538,31 @@

*/
/**
* Returns `true` when the host is Ubuntu 23.10+ / Kubuntu 25.10+ (or any
* Linux with the AppArmor knob enabled), where unprivileged user
* namespaces are blocked and Chromium's sandbox cannot initialize for
* non-root processes.
*
* Probes `/proc/sys/kernel/apparmor_restrict_unprivileged_userns` —
* value `1` means blocked. Missing file (older kernels, non-AppArmor
* distros) returns `false` so we don't false-positive into auto-
* `--no-sandbox` on systems that don't need it. Issue #52.
*
* Side-effect-free, sub-millisecond on Linux. Returns `false` on every
* non-Linux host.
*
* @internal
*/
export async function apparmorRestrictsUserns(): Promise<boolean> {
if (process.platform !== "linux") return false;
try {
const buf = await readFile("/proc/sys/kernel/apparmor_restrict_unprivileged_userns", "utf8");
return buf.trim() === "1";
} catch {
// ENOENT, EACCES, or anything else — assume not blocked. The
// post-spawn `diagnoseEarlyExitTail` still catches the "No usable
// sandbox" stderr pattern as the safety net.
return false;
}
}
export function diagnoseEarlyExitTail(tail: string): string {

@@ -519,2 +579,26 @@ if (/running.*root.*without.*--no-sandbox|--no-sandbox.*required/i.test(tail)) {

}
// Ubuntu 23.10+ / Kubuntu 25.10+ — AppArmor blocks unprivileged user
// namespaces. Chromium emits "FATAL: ... No usable sandbox!" with a
// pointer to its own AppArmor docs. If the proactive AppArmor probe in
// spawnChromium() missed (e.g., AppArmor on without the
// `apparmor_restrict_unprivileged_userns` sysctl set), surface the
// remediation here. Issue #52.
if (/No usable sandbox|unprivileged.+user namespace|apparmor/i.test(tail)) {
return (
"\n\nChromium's user-namespace sandbox cannot initialize on this host.\n" +
"This is the Ubuntu 23.10+ / Kubuntu 25.10+ default — AppArmor blocks\n" +
"unprivileged user namespaces. Check the knob with:\n\n" +
" cat /proc/sys/kernel/apparmor_restrict_unprivileged_userns # 1 = blocked\n\n" +
"Fixes (preferred → workaround):\n" +
" 1. Install an AppArmor profile for the Chromium binary —\n" +
" https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md\n" +
" 2. Temporarily lift the restriction (system-wide; weakens host security):\n" +
" sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0\n" +
" 3. Pass args: ['--no-sandbox'] to mochi.launch() — fingerprint leak\n" +
" (PLAN §8.6), acceptable for local dev / CI, not stealth-critical prod.\n" +
" mochi auto-applies this fallback when it detects the AppArmor\n" +
" restriction; if you saw this error, the detection missed —\n" +
" please file an issue with `cat /proc/sys/kernel/apparmor_restrict_unprivileged_userns`."
);
}
const libMatch = /error while loading shared libraries:\s+([^\s:]+)/i.exec(tail);

@@ -521,0 +605,0 @@ if (libMatch !== null) {

/** Single source of truth for the @mochi.js/core package version string. */
export const VERSION = "0.9.3" as const;
export const VERSION = "0.9.4" as const;