From f59ec700114f86825ff741b752624044159b1761 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Wed, 3 Sep 2025 13:44:58 +0000 Subject: [PATCH] test: using nixos-test --- .github/workflows/nix-flake-check.yml | 13 + flake.nix | 259 +--------------- tests.nix | 408 ++++++++++++++++++++++++++ 3 files changed, 434 insertions(+), 246 deletions(-) create mode 100644 .github/workflows/nix-flake-check.yml create mode 100644 tests.nix diff --git a/.github/workflows/nix-flake-check.yml b/.github/workflows/nix-flake-check.yml new file mode 100644 index 0000000..b1d10d5 --- /dev/null +++ b/.github/workflows/nix-flake-check.yml @@ -0,0 +1,13 @@ +name: nix-flake-check + +on: + pull_request: + push: + +jobs: + nix-flake-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v31 + - run: nix flake check -L . diff --git a/flake.nix b/flake.nix index 74a2d31..f9a626f 100644 --- a/flake.nix +++ b/flake.nix @@ -9,265 +9,32 @@ outputs = { nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem ( + # TODO: explicitly define linux systems, not darwin system: + let pkgs = nixpkgs.legacyPackages.${system}; + in rec { - - lib = import ./lib.nix { inherit pkgs; }; - packages = rec { wrap = pkgs.callPackage ./package.nix { }; default = wrap; }; - devShells.default = import ./shell.nix { inherit pkgs; }; - - checks = - let - wrap-bin = "${packages.wrap}/bin/wrap"; - bash-bin = "${pkgs.bash}/bin/bash"; - - tests = [ - { - name = "env-home-is-always-exposed"; - test = ''HOME=/homedir ${wrap-bin} ${bash-bin} -c 'echo $HOME' | grep homedir > $out''; - } - { - name = "env-editor-is-always-exposed"; - test = ''EDITOR=myeditor ${wrap-bin} ${bash-bin} -c 'echo $EDITOR' | grep myeditor > $out''; - } - { - name = "user-name-is-hidden"; - test = '' - ${wrap-bin} whoami 2> error-msg || true - cat error-msg | grep "cannot find name for user ID" > $out - ''; - } - { - name = "u-exposes-user-name"; - test = ''${wrap-bin} -u whoami > $out''; - } - { - name = "env-wayland-display-is-hidden"; - test = '' - WAYLAND_DISPLAY=wl-0 ${wrap-bin} ${bash-bin} -c 'set -u; echo $WAYLAND_DISPLAY' 2> error-msg || true - cat error-msg | grep "WAYLAND_DISPLAY: unbound variable" > $out - ''; - } - { - name = "d-exposes-env-wayland-display"; - test = '' - export XDG_RUNTIME_DIR="/tmp" - export WAYLAND_DISPLAY="wl-0" - mkdir -p $XDG_RUNTIME_DIR - touch $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY - ${wrap-bin} -d ${bash-bin} -c 'echo $WAYLAND_DISPLAY' | grep wl-0 > $out - ''; - } - { - name = "d-exposes-env-x11-display"; - test = '' - export DISPLAY=":0" - ${wrap-bin} -d ${bash-bin} -c 'echo $DISPLAY' | grep ":0" > $out - ''; - } - { - name = "d-exposes-socket-x11"; - test = '' - mkdir -p /tmp/.X11-unix - touch /tmp/.X11-unix/X12345 - export DISPLAY=":12345" - ${wrap-bin} -d ${bash-bin} -c 'ls /tmp/.X11-unix/X12345' > $out - rm /tmp/.X11-unix/X12345 - ''; - } - { - name = "d-exposes-xauthority"; - test = '' - export DISPLAY=":12345" - export HOME=/tmp/home - mkdir -p $HOME - touch $HOME/.Xauthority - ${wrap-bin} -d ${bash-bin} -c 'cat $HOME/.Xauthority' > $out - ''; - } - { - name = "d-exposes-custom-xauthority"; - test = '' - export DISPLAY=":12345" - export XAUTHORITY="myxauthfile" - export HOME=/tmp/home - mkdir -p $HOME - touch $HOME/$XAUTHORITY - ${wrap-bin} -d ${bash-bin} -c 'cat $HOME/.Xauthority' > $out - ''; - } - { - name = "r-exposes-path-readonly"; - test = '' - mkdir -p /tmp/some-dir - echo "file-content" > /tmp/some-dir/test-file - ${wrap-bin} -r /tmp/some-dir ${bash-bin} -c 'cat /tmp/some-dir/test-file' | grep "file-content" - ${wrap-bin} -r /tmp/some-dir ${bash-bin} -c 'echo more >> /tmp/some-dir/test-file' 2> error-msg || true - cat error-msg | grep "/tmp/some-dir/test-file: Read-only file system" > $out - ''; - } - { - name = "w-exposes-path-readwrite"; - test = '' - mkdir -p /tmp/some-dir - echo "file-content" > /tmp/some-dir/test-file - ${wrap-bin} -w /tmp/some-dir ${bash-bin} -c 'cat /tmp/some-dir/test-file' | grep "file-content" - ${wrap-bin} -w /tmp/some-dir ${bash-bin} -c 'echo more >> /tmp/some-dir/test-file' - cat /tmp/some-dir/test-file | grep "more" > $out - ''; - } - { - name = "cwd-exposed-by-default"; - test = '' - mkdir -p /tmp/some-dir - cd /tmp/some-dir - echo "file-content" > test-file - ${wrap-bin} ${bash-bin} -c 'cat test-file' | grep "file-content" > $out - ''; - } - { - name = "cwd-not-exposed-by-p"; - test = '' - mkdir -p /tmp/some-dir - cd /tmp/some-dir - echo "file-content" > test-file - ${wrap-bin} -p ${bash-bin} -c 'cat test-file; echo $?' | grep 1 > $out - ''; - } - { - name = "-p-cds-to-root"; - test = '' - mkdir -p /tmp/new-home - export HOME=/tmp/new-home - ${wrap-bin} -p ${bash-bin} -c 'pwd' | grep / > $out - ''; - } - - { - name = "cwd not shared implicitly for home directories"; - test = - # setup prerequisites - '' - # Setup a home directory and put something in. We expect - # this to NOT be visible in the sandbox because it was not - # shared explicitly and home directories are expluded from - # implicit sharing. - mkdir -p /tmp/new-home - export HOME=/tmp/new-home - touch /tmp/new-home/something-in-home - - # Make the home directory the cwd - cd $HOME - '' + - - # prerequisite checks - '' - pwd | grep '^/tmp/new-home$' \ - || (echo 'Unexpected: Home directory is not cwd outside sandbox'; false) - - ls -l /tmp | grep '[[:space:]]new-home$' \ - || (echo 'Unexpected: Home directory outside sandbox not found'; false) - - ls -l $HOME | grep '[[:space:]]something-in-home$' \ - || (echo 'Unexpected: File in $HOME outside sandbox not found'; false) - '' + - - # test - '' - # expect the cwd to be /, because $HOME as cwd is excluded from implicit sharing - ${wrap-bin} ${bash-bin} -c 'pwd' | grep '^/$' \ - || (echo 'Unexpected: Cwd in sandbox is not /'; false) - - ${wrap-bin} ${bash-bin} -c 'ls -l $HOME' | grep '^total 0$' \ - || (echo 'Unexpected: Sandbox $HOME is not empty'; false) - - echo 'test-success' > $out - ''; - } - - { - name = "parameter -f forces to share the cwd $HOME, even though it is excluded from sharing as cwd implicitly"; - test = - # setup prerequisites - '' - # Setup a home directory and put something in. We expect - # this to be visible in the sandbox because it was shared - # explicitly implicit sharing. - mkdir -p /tmp/new-home - export HOME=/tmp/new-home - touch /tmp/new-home/something-in-home - - # Make the home directory the cwd - cd $HOME - '' + - - # prerequisite checks - '' - pwd | grep '^/tmp/new-home$' \ - || (echo 'Unexpected: Home directory is not cwd outside sandbox'; false) - - ls -l /tmp | grep '[[:space:]]new-home$' \ - || (echo 'Unexpected: Home directory outside sandbox not found'; false) - - ls -l $HOME | grep '[[:space:]]something-in-home$' \ - || (echo 'Unexpected: File in $HOME outside sandbox not found'; false) - '' + - - # test - '' - # expect the cwd to be $HOME - ${wrap-bin} -f ${bash-bin} -c 'pwd' | grep '^/tmp/new-home$' \ - || (echo 'Unexpected: Cwd in sandbox is not $HOME'; false) + lib = import ./lib.nix { + inherit pkgs; + }; - ${wrap-bin} -f ${bash-bin} -c 'ls $HOME' | grep '^something-in-home$' \ - || (echo 'Unexpected: Sandbox $HOME is empty'; false) + devShells.default = import ./shell.nix { + inherit pkgs; + }; - echo 'test-success' > $out - ''; - } + checks.default = import ./tests.nix { + inherit pkgs; + inherit (packages) wrap; + }; - { - name = "parameter -f forces to share the cwd /, even though it is excluded from sharing as cwd implicitly"; - test = - # setup prerequisits - '' - # / is a directory expluded from implicit cwd sharing - cd / - '' + - # prerequisit checks - '' - pwd | grep "^/$" \ - || (echo 'Unexpected: Cwd to be / outside sandbox'; false) - ls -l | grep "[[:space:]]bin$" \ - || (echo 'Unexpected: Bin dir is missing in / outside sandbox'; false) - '' + - # test - '' - ${wrap-bin} -f ${bash-bin} -c 'pwd' | grep '^/$' 2> /dev/null \ - || (echo 'Unexpected: Cwd in sandbox is not /'; false) - ${wrap-bin} -f ${bash-bin} -c 'ls -l' | grep 'bin$' 2> /dev/null \ - || (echo 'Unexpected: Bin dir not in / inside sandbox'; false) - echo 'test-success' > $out - ''; - } - ]; - in - builtins.listToAttrs ( - map - (t: { - name = t.name; - value = pkgs.runCommand t.name { } t.test; - }) - tests - ); } ); } diff --git a/tests.nix b/tests.nix new file mode 100644 index 0000000..2c2dbea --- /dev/null +++ b/tests.nix @@ -0,0 +1,408 @@ +{ pkgs, wrap }: + +pkgs.nixosTest { + name = "mytest"; + + nodes.machine = { config, pkgs, ... }: { + system.stateVersion = "24.05"; + networking.dhcpcd.enable = false; # boots faster + environment.systemPackages = [ wrap ]; + + users.users.alice = { + isNormalUser = true; + createHome = true; + }; + }; + + testScript = /* python */ '' + machine.wait_for_unit("default.target") + + # Run a script as alice + # This function wraps a bash script with su in order to run as alice. + # Single quotes are escaped in the script, and the script is handed to su + # in $'...' ansi quoting in order to allow escaped single quotes inside the + # single quoted string. The script is run in 'bash strict mode'. + def as_alice(script): + script = script.replace("'", r"\'") + script = "set -euo pipefail\n" + script + return ( "su " + "--login alice " + "--shell ${pkgs.bash}/bin/bash " + f"--command $'{script}'" + ) + + # as_alice = lambda script: ( + # f"su --login alice --shell ${pkgs.bash}/bin/bash --command $'{script.replace("'", r"\'")}'" + # ) + + with subtest("Environment variable $HOME is always exposed"): + machine.succeed(as_alice(""" + # ensure $HOME is set + echo $HOME | grep '^/home/alice$' || + (echo 'Unexpected: $HOME is not set outside sandbox'; false) + + # ensure $HOME remains set in sandbox + wrap bash -c 'echo $HOME' | grep '^/home/alice$' || + (echo 'Unexpected: $HOME is unset in sandbox'; false) + """)) + + with subtest("Environment variable $EDITOR is always exposed"): + machine.succeed(as_alice(""" + # ensure $EDITOR is set + export EDITOR=vim + echo $EDITOR | grep '^vim$' || + (echo 'Unexpected: $VIM is not set outside sandbox'; false) + + # ensure $EDITOR remains set in sandbox + wrap bash -c 'echo $EDITOR' | grep '^vim$' || + (echo 'Unexpected: $EDITOR is unset in sandbox: '; false) + """)) + + with subtest("Username is hidden in sandbox, whoami does not work"): + machine.succeed(as_alice(""" + # ensure `whoami` works outside sandbox + whoami | grep 'alice' || + (echo 'Unexpected: whoami does not work outside sandbox'; false) + + # ensure `whoami` does not work in sandbox + ! wrap whoami || + (echo 'Unexpected: whoami works in sandbox'; false) + + ! wrap whoami 2>&1 | grep "cannot find name for user ID" || + (echo 'Unexpected: whoami seems to work in sandbox'; false) + """)) + + with subtest("-u exposes username in sandbox, whoami does work"): + machine.succeed(as_alice(""" + # ensure `whoami` works outside sandbox + whoami | grep 'alice' || + (echo 'Unexpected: whoami does not work outside sandbox'; false) + + # ensure `whoami` does work in sandbox + wrap -u whoami || + (echo 'Unexpected: whoami does not work in sandbox'; false) + + # ensure `whoami` returns username in sandbox + wrap -u whoami | grep "^alice$" || + (echo 'Unexpected: whoami does not return username in sandbox'; false) + """)) + + + with subtest("Environment variable $WAYLAND_DISPLAY is hidden by default"): + machine.succeed(as_alice(""" + # ensure $WAYLAND_DISPLAY is set outside sandbox + export WAYLAND_DISPLAY=wl-0 + echo $WAYLAND_DISPLAY | grep '^wl-0$' || + (echo 'Unexpected: WAYLAND_DISPLAY is not set outside sandbox'; false) + + # ensure $WAYLAND_DISPLAY is unset in sandbox + ! (wrap bash -c 'echo $WAYLAND_DISPLAY' | grep '^wl-0$') || + (echo 'Unexpected: WAYLAND_DISPLAY is set in sandbox'; false) + """)) + + with subtest("-d exposes $WAYLAND_DISPLAY in sandbox"): + machine.succeed(as_alice(""" + # ensure $WAYLAND_DISPLAY is set outside sandbox + export WAYLAND_DISPLAY=wl-0 + echo $WAYLAND_DISPLAY | grep '^wl-0$' || + (echo 'Unexpected: WAYLAND_DISPLAY is not set outside sandbox'; false) + + # ensure $WAYLAND_DISPLAY is set in sandbox + wrap -d bash -c 'echo $WAYLAND_DISPLAY' | grep '^wl-0$' || + (echo 'Unexpected: WAYLAND_DISPLAY is not set in sandbox'; false) + """)) + + with subtest("-d exposes wayland socket in sandbox"): + machine.succeed(as_alice(""" + # ensure $WAYLAND_DISPLAY is set outside sandbox + export WAYLAND_DISPLAY=wl-12345 + echo $WAYLAND_DISPLAY | grep '^wl-12345$' || + (echo 'Unexpected: WAYLAND_DISPLAY is not set outside sandbox'; false) + + # create wayland socket mock + export XDG_RUNTIME_DIR=/tmp/wayland + mkdir -p $XDG_RUNTIME_DIR + touch $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY + echo $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY | grep '^/tmp/wayland/wl-12345$' || + (echo 'Unexpected: WAYLAND socket mock does not exist outside sandbox'; false) + + # ensure $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY is set in sandbox + wrap -d bash -c 'echo $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY' | grep '^/tmp/wayland/wl-12345$' || + (echo 'Unexpected: $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY is not set in sandbox'; false) + + # ensure wayland socket mock exists in sandbox + wrap -d bash -c 'ls $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY' | grep '^/tmp/wayland/wl-12345$' || + (echo 'Unexpected: wayland socket mock does not exist in sandbox'; false) + """)) + + with subtest("Environment variable $DISPLAY is hidden by default"): + machine.succeed(as_alice(""" + # ensure $DISPLAY is set outside sandbox + export DISPLAY=:0 + echo $DISPLAY | grep '^:0$' || + (echo 'Unexpected: $DISPLAY is not set outside sandbox'; false) + + # ensure $DISPLAY is unset in sandbox + ! (wrap bash -c 'echo $DISPLAY' | grep '^:0$') || + (echo 'Unexpected: $DISPLAY is set in sandbox'; false) + """)) + + with subtest("-d exposes $DISPLAY in sandbox"): + machine.succeed(as_alice(""" + # ensure $DISPLAY is set outside sandbox + export DISPLAY=:0 + echo $DISPLAY | grep '^:0$' || + (echo 'Unexpected: $DISPLAY is not set outside sandbox'; false) + + # ensure $DISPLAY is set in sandbox + wrap -d bash -c 'echo $DISPLAY' | grep '^:0$' || + (echo 'Unexpected: $DISPLAY is not set in sandbox'; false) + """)) + + with subtest("-d exposes X11 socket in sandbox"): + machine.succeed(as_alice(""" + mkdir -p /tmp/.X11-unix + touch /tmp/.X11-unix/X12345 + + # ensure $DISPLAY is set outside sandbox + export DISPLAY=:12345 + echo $DISPLAY | grep '^:12345$' || + (echo 'Unexpected: $DISPLAY is not set outside sandbox'; false) + + # ensure $DISPLAY socket is visible in sandbox + wrap -d bash -c 'ls /tmp/.X11-unix/X12345' | grep 'X12345' || + (echo 'Unexpected: $DISPLAY socket is not visible in sandbox'; false) + """)) + + with subtest("-d exposes .Xauthority in sandbox"): + machine.succeed(as_alice(""" + # create mock home + export HOME=/tmp/home + mkdir -p $HOME + + # create mock Xauthority file + touch $HOME/.Xauthority + + # ensure Xauthority file exists in sandbox + wrap -d bash -c 'ls $HOME/.Xauthority' | grep '.Xauthority$' || + (echo 'Unexpected: .Xauthority is not visible in sandbox'; false) + """)) + + with subtest("-d exposes custom Xauthority file in sandbox"): + machine.succeed(as_alice(""" + # create mock home + export HOME=/tmp/home + mkdir -p $HOME + + # create mock custom Xauthority file + touch $HOME/myxauthfile + echo "mysecret" > $HOME/myxauthfile + export XAUTHORITY=myxauthfile + + # ensure custom Xauthority file exists in sandbox + wrap -d bash -c 'cat $HOME/.Xauthority' | grep '^mysecret$' || + (echo 'Unexpected: custom .Xauthority is not visible in sandbox'; false) + """)) + + with subtest("-r exposes path readonly"): + machine.succeed(as_alice(""" + # create some test file + mkdir -p /tmp/some-dir + echo "file-content" > /tmp/some-dir/test-file + + # try to read the test file + wrap -r /tmp/some-dir bash -c 'cat /tmp/some-dir/test-file' | grep '^file-content$' || + (echo 'Unexpected: Did not get expected output when trying to read from readonly path'; false) + + # try to write to the test file + wrap -r /tmp/some-dir bash -c 'echo "Hello World" > /tmp/some-dir/test-file' 2> error || true + cat error | grep '/tmp/some-dir/test-file: Read-only file system$' || + (echo 'Unexpected: Did not get expected error when trying to write to readonly path'; false) + """)) + + with subtest("-w exposes path readwrite"): + machine.succeed(as_alice(""" + # create some test file + mkdir -p /tmp/some-dir + echo "file-content" > /tmp/some-dir/test-file + + # try to read the test file + wrap -w /tmp/some-dir bash -c 'cat /tmp/some-dir/test-file' | grep '^file-content$' || + (echo 'Unexpected: Did not get expected output when trying to read from readwrite path'; false) + + # try to write to the test file + wrap -w /tmp/some-dir bash -c 'echo "Hello World" > /tmp/some-dir/test-file' || + (echo 'Unexpected: Cannot write to readwrite path'; false) + + cat /tmp/some-dir/test-file | grep '^Hello World$' || + (echo 'Unexpected: Did not get expected output when reading freshly written file'; false) + """)) + + with subtest("cwd is exposed by default"): + machine.succeed(as_alice(""" + mkdir -p /tmp/some-dir + cd /tmp/some-dir + echo "file-content" > test-file + + # Expect cat to succeed inside sandbox + wrap bash -c 'cat test-file' | grep '^file-content$' || + (echo 'Unexpected: cwd not exposed by default'; false) + """)) + + with subtest("-p does not expose cwd"): + machine.succeed(as_alice(""" + mkdir -p /tmp/some-dir + cd /tmp/some-dir + echo "file-content" > test-file + + # Expect cat to fail inside sandbox when -p is used + wrap -p bash -c 'cat test-file; echo $?' | grep '^1$' || + (echo 'Unexpected: cwd exposed when using -p'; false) + """)) + + with subtest("-p cds to root"): + machine.succeed(as_alice(""" + mkdir -p /tmp/new-home + export HOME=/tmp/new-home + + # Expect pwd to return / in sandbox + wrap -p bash -c 'pwd' | grep '^/$' || + (echo 'Unexpected: -p did not change cwd as expected'; false) + """)) + + with subtest("$HOME as cwd is not shared implicitly"): + machine.succeed(as_alice(""" + # setup prerequisites + mkdir -p /tmp/new-home + export HOME=/tmp/new-home + touch /tmp/new-home/something-in-home + cd $HOME + + # expect cwd to be changed to / + wrap bash -c 'pwd' | grep '^/$' || + (echo 'Unexpected: Cwd in sandbox is not /'; false) + + # expect $HOME to be empty + wrap bash -c 'ls -l $HOME' | grep '^total 0$' || + (echo 'Unexpected: Sandbox $HOME is not empty'; false) + """)) + + with subtest("/etc as cwd is excluded from implicit sharing"): + machine.succeed(as_alice(""" + cd /etc + wrap bash -c 'pwd' | grep '^/$' || + (echo 'Unexpected: /etc shared implicitly as cwd'; false) + """)) + + with subtest("-f forces sharing HOME as cwd"): + machine.succeed(as_alice(""" + # setup prerequisites + mkdir -p /tmp/new-home + export HOME=/tmp/new-home + touch /tmp/new-home/something-in-home + cd $HOME + + # expect cwd to be $HOME + wrap -f bash -c 'pwd' | grep '^/tmp/new-home$' || + (echo 'Unexpected: Cwd in sandbox is not $HOME'; false) + + # expect file in $HOME + wrap -f bash -c 'ls $HOME' | grep '^something-in-home$' || + (echo 'Unexpected: Sandbox $HOME is empty'; false) + """)) + + with subtest("Network files are available only with -n"): + machine.succeed(as_alice(""" + # Without -n, resolv.conf should not be visible + ! wrap bash -c 'ls /etc/resolv.conf' || + (echo 'Unexpected: /etc/resolv.conf visible without -n'; false) + + # With -n, resolv.conf and /etc/ssl should be visible + wrap -n bash -c 'ls /etc/resolv.conf' | grep '^/etc/resolv.conf$' || + (echo 'Unexpected: /etc/resolv.conf not visible with -n'; false) + wrap -n bash -c 'test -d /etc/ssl && echo ok' | grep '^ok$' || + (echo 'Unexpected: /etc/ssl not visible with -n'; false) + """)) + + with subtest("Audio sockets visible only with -a"): + machine.succeed(as_alice(""" + export XDG_RUNTIME_DIR=/tmp/xdg + mkdir -p "$XDG_RUNTIME_DIR/pulse" + touch "$XDG_RUNTIME_DIR/pulse/native" + touch "$XDG_RUNTIME_DIR/pipewire-0" + touch "$XDG_RUNTIME_DIR/pipewire-0.lock" + + # Without -a, these paths should not be visible in sandbox + ! (wrap bash -c 'ls $XDG_RUNTIME_DIR/pulse/native') || + (echo 'Unexpected: pulse/native visible without -a'; false) + ! (wrap bash -c 'ls $XDG_RUNTIME_DIR/pipewire-0') || + (echo 'Unexpected: pipewire-0 visible without -a'; false) + ! (wrap bash -c 'ls $XDG_RUNTIME_DIR/pipewire-0.lock') || + (echo 'Unexpected: pipewire-0.lock visible without -a'; false) + + # With -a, they should be visible + wrap -a bash -c 'ls $XDG_RUNTIME_DIR/pulse/native' | grep 'pulse/native' || + (echo 'Unexpected: pulse/native not visible with -a'; false) + wrap -a bash -c 'ls $XDG_RUNTIME_DIR/pipewire-0' | grep 'pipewire-0$' || + (echo 'Unexpected: pipewire-0 not visible with -a'; false) + wrap -a bash -c 'ls $XDG_RUNTIME_DIR/pipewire-0.lock' | grep 'pipewire-0.lock$' || + (echo 'Unexpected: pipewire-0.lock not visible with -a'; false) + """)) + + with subtest("DBus session socket visible only with -b"): + machine.succeed(as_alice(""" + # Create a fake DBus session socket and export address + touch /tmp/dbus-sock + export DBUS_SESSION_BUS_ADDRESS="unix:path=/tmp/dbus-sock" + + # Without -b, it should not be visible + ! (wrap bash -c 'ls /tmp/dbus-sock') || + (echo 'Unexpected: DBus socket visible without -b'; false) + + # With -b, it should be visible + wrap -b bash -c 'ls /tmp/dbus-sock' | grep '/tmp/dbus-sock' || + (echo 'Unexpected: DBus socket not visible with -b'; false) + """)) + + with subtest("Env var passthrough via -e and default stripping"): + machine.succeed(as_alice(""" + export SECRET_VAR=topsecret + + # By default SECRET_VAR should be stripped + wrap bash -c 'echo ''${SECRET_VAR:-unset}' | grep '^unset$' || + (echo 'Unexpected: SECRET_VAR visible without -e'; false) + + # With -e SECRET_VAR it should be visible + wrap -e SECRET_VAR bash -c 'echo ''${SECRET_VAR:-unset}' | grep '^topsecret$' || + (echo 'Unexpected: SECRET_VAR not visible with -e'; false) + """)) + + with subtest("NIX_PROFILES are ro-bound and not writable"): + machine.succeed(""" + mkdir -p /profile + """) + machine.succeed(as_alice(""" + export NIX_PROFILES="''${NIX_PROFILES} /profile" + + # Directory should be visible in sandbox + wrap bash -c 'ls -l /profile' | grep '^total 0$' || + (echo 'Unexpected: NIX_PROFILES dir not visible in sandbox'; false) + + # Writing inside should fail due to ro-bind + wrap bash -c 'echo hi > /profile/file' 2> error || true + cat error | grep '/profile/file: Read-only file system$' || + (echo 'Unexpected: /profile is writable in sandbox'; false) + """)) + + with subtest("HOME is created and writable inside sandbox"): + machine.succeed(as_alice(""" + export HOME=/tmp/new-home-absent + rm -rf "$HOME" + + # With -p (no cwd sharing), bwrap --dir should create HOME inside sandbox and be writable + wrap -p bash -c 'test -d "$HOME" -a -w "$HOME" && echo ok' | grep '^ok$' || + (echo 'Unexpected: $HOME not created or not writable inside sandbox'; false) + """)) + ''; +} +