Merge the three runtime-validation files into one concise record, update the interactive browser and Zoom results, and document the intentional Chromium host-loopback and Brave/Edge no-media exceptions. Tighten the remaining cookie-crypto workaround and the smoke-test prerequisites. Assisted-by: DeepSeek Harness (deepseek-flash)
4.0 KiB
Runtime validation
Nixpak wrappers are accepted in three layers:
- Policy/build:
just testevaluates every wrapper andpolicy.nixrejects broad mounts, system D-Bus, keyring access and device grants outside each app's declared scope. - Disposable smoke test:
verify-chromium.pylaunches a Chromium-family wrapper with a temporary HOME and validates renderer sandboxing, environment clearing, bind scope, profile persistence and loopback policy. - Interactive desktop check: launch the real app, use its normal file/network/device workflows, and confirm the sandbox does not break required integration.
Current status
| App | Policy/build | Runtime |
|---|---|---|
| Firefox | Pass | Interactive pass |
| Chromium | Pass | Automated smoke + interactive pass |
| Chrome | Pass | Interactive pass |
| Brave Origin | Pass | Interactive pass; backup browser without media grants |
| Edge | Pass | Interactive pass; backup browser without media grants |
| Zoom | Pass | Interactive pass |
| QQ / Telegram | Pass | Interactive startup/tray pass |
| WeChat (native nixpak) | Pass | Interactive Wayland validation pending |
Intentional exceptions:
- Chromium keeps host loopback and personal files for local development. Firefox has the same exception. Chrome, Brave Origin and Edge use pasta-isolated loopback.
- Brave Origin and Edge are backup browsers. They intentionally have no PulseAudio, Camera, ScreenCast or MPRIS grants.
Browser smoke test
Build a wrapper from the locked inputs:
APP=chromium nix build --impure --out-link "browser-validation/$APP" --expr '
let
flake = builtins.getFlake (toString ./.);
pkgs = import flake.inputs.nixpkgs {
system = builtins.currentSystem;
config.allowUnfree = true;
};
in
pkgs.callPackage (./hardening/nixpaks + "/${builtins.getEnv "APP"}.nix") {
mkNixPak = flake.inputs.nixpak.lib.nixpak { inherit pkgs; inherit (pkgs) lib; };
}
'
python3 hardening/tests/verify-chromium.py "browser-validation/$APP/bin/$APP"
The script writes its disposable HOME under /var/tmp; the sandbox's private /tmp would otherwise
hide profile binds. Pasta-wrapped browsers also require a usable /dev/net/tun on the host. The
script checks live renderer Seccomp=2 and NoNewPrivs=1, absence of --no-sandbox, canary-free
environments, effective bwrap arguments, profile persistence and loopback reachability.
Chromium-family browsers use the app-scoped Secret portal for v12 cookie encryption; they do not
receive org.freedesktop.secrets. If the portal is unavailable, Chromium can fall back to weaker
v10 storage; monitor Local State.os_crypt.portal.prev_init_success. Firefox uses NSS storage
instead. Browserpass separately exposes the password store and GPG-agent socket to
Firefox/Chromium/Chrome only.
Zoom
Zoom keeps its upstream FHS runtime inside the outer nixpak boundary. The nested sandbox propagates
/.flatpak-info, uses pasta-isolated networking, and binds only DISPLAY, XAUTHORITY,
/dev/video0, /dev/video1, DRM sysfs and the declared app directories. X11 remains a
shared-display trust boundary: ScreenCast portal permission does not prevent capture through the X11
connection. Interactive login, audio, camera, screen sharing and file workflows passed.
Commands
just fmt
just test
git diff --check
ruff format --check hardening/tests/verify-chromium.py
ruff check hardening/tests/verify-chromium.py
prettier --check hardening/tests/runtime-validation.md