Skip to content
7 changes: 7 additions & 0 deletions .github/workflows/cef-learning-harness.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ jobs:
- name: Clean-machine Linux dependency inventory
run: node scripts/cef/check-linux-runtime-deps.mjs

# QNBS-v3: diagnostic-only, no behavior change — real evidence on whether this runner can
# even support Chromium's Linux sandbox (Sandbox posture row, native-readiness.md, "Not yet
# attempted") before any attempt to actually enable it. Non-fatal by design.
- name: Linux sandbox feasibility inventory (diagnostic only)
run: node scripts/cef/check-linux-sandbox-inventory.mjs

- name: Restore CEF SDK cache
id: cef-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
Expand Down Expand Up @@ -227,5 +233,6 @@ jobs:
echo "- worldscript_host built and repeated launch/close cycles proven against the real production bundle (dist/), under Xvfb." >> "$GITHUB_STEP_SUMMARY"
echo "- Linux runtime linkage (ldd against the shipped worldscript_host + libcef.so, not just dpkg package presence): see the \"Linux runtime linkage check\" step above." >> "$GITHUB_STEP_SUMMARY"
echo "- Crash-symbolization proof: a self-induced browser-process crash resolved via dump_syms + minidump-stackwalk against our own DWARF debug info — Chromium/CEF-internal frames remain unsymbolized (no debug-symbols archive is published for this distribution)." >> "$GITHUB_STEP_SUMMARY"
echo "- Linux sandbox feasibility inventory (diagnostic only, no sandbox behavior attempted yet): see the \"Linux sandbox feasibility inventory\" step above." >> "$GITHUB_STEP_SUMMARY"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
echo "- Wayland smoke (best-effort, roadmap §44.2): \`${{ steps.wayland-smoke.outcome }}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- Not yet in scope: X11/Wayland matrix beyond this one runner, sandbox posture, accessibility-tree observability (AT-SPI — state enablement is proven, see the launch-cycle-proof step)." >> "$GITHUB_STEP_SUMMARY"
74 changes: 74 additions & 0 deletions scripts/cef/check-linux-sandbox-inventory.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env node
/**
* CEF Linux sandbox feasibility inventory (Wave 2, roadmap Sandbox posture item — "Not yet
* attempted" in docs/architecture/native-readiness.md).
*
* Diagnostic-only, non-invasive — reports real evidence about whether this runner can support
* Chromium's Linux sandbox, before any attempt to actually enable it (CefSettings.no_sandbox is
* currently `true` unconditionally, apps/desktop-cef/src/main.cpp, ADR-0020). Never changes
* worldscript_host's build or runtime behavior.
*
* Background (confirmed against CEF's own docs/sandbox_setup.md and Chromium's
* docs/linux_sandboxing.md before writing this — CEF has no Linux-specific sandbox API, unlike
* cef_sandbox_win.h/cef_sandbox_mac.h; the sandbox is entirely a Chromium-internal mechanism,
* toggled only via CefSettings.no_sandbox):
* - Layer-1 (process/namespace isolation): the legacy setuid `chrome-sandbox` helper (needs
* root ownership + the setuid bit), or the modern unprivileged user-namespaces sandbox
* (preferred automatically since Chromium M-43 if the kernel/policy allows it — no setuid
* binary needed at all).
* - Layer-2 (seccomp-bpf syscall filtering): independent of layer-1, needs Linux kernel >= 3.5.
*
* This script checks real, functional evidence for layer-1 feasibility — not just documentation
* claims — since a sysctl value alone doesn't prove unprivileged namespace creation actually
* succeeds (AppArmor profiles or container restrictions can block it even when the sysctl says
* "enabled").
*
* Run: node scripts/cef/check-linux-sandbox-inventory.mjs
*/
import { execFileSync } from 'node:child_process';
import fs from 'node:fs';

function tryRun(cmd, args) {
try {
return execFileSync(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] })
.toString()
.trim();
} catch {
return null;
}
}

console.log('[check-sandbox-inventory] CEF Linux sandbox feasibility inventory:');

const kernelRelease = tryRun('uname', ['-r']);
console.log(` kernel release: ${kernelRelease ?? '(uname failed)'}`);

// QNBS-v3: this sysctl is a Debian/Ubuntu-specific patch, not present on every distro/kernel —
// its absence is itself informative (means the mainline kernel default applies), not an error.
let sysctlValue = null;
try {
sysctlValue = fs.readFileSync('/proc/sys/kernel/unprivileged_userns_clone', 'utf8').trim();
console.log(` kernel.unprivileged_userns_clone: ${sysctlValue}`);
} catch {
console.log(' kernel.unprivileged_userns_clone: (sysctl not present on this kernel/distro)');
}

// QNBS-v3: functional test, not just reading the sysctl — AppArmor profiles or container-level
// restrictions (e.g. missing CAP_SYS_ADMIN, seccomp policies on the runner itself) can block
// unprivileged namespace creation even when the sysctl claims it's allowed.
const unshareTest = tryRun('unshare', ['--user', '--pid', '--fork', 'true']);
const unshareWorks = unshareTest !== null;
console.log(
` unshare --user --pid --fork (functional test): ${unshareWorks ? 'succeeded' : 'FAILED'}`,
);

const aaEnabled = fs.existsSync('/sys/module/apparmor/parameters/enabled')
? fs.readFileSync('/sys/module/apparmor/parameters/enabled', 'utf8').trim()
: null;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
console.log(` AppArmor module enabled: ${aaEnabled ?? '(not present)'}`);

console.log(
`\n[check-sandbox-inventory] Verdict: unprivileged user-namespace sandboxing appears ` +
`${unshareWorks ? 'FEASIBLE' : 'NOT FEASIBLE'} on this runner (functional test, not just a sysctl read). ` +
`This is informational only — no sandbox behavior was changed by running this script.`,
);
Loading