Skip to content

Commit 57470be

Browse files
authored
test(e2e): add non-root sandbox smoke test (NVIDIA#3166)
<!-- markdownlint-disable MD041 --> ## Summary Adds the `test-non-root-sandbox-smoke` test from NVIDIA#2571 — a PR-gate job that runs the production image under `-security-opt no-new-privileges` to catch NVIDIA#2472 and NVIDIA#2482 regressions, without OpenShell, NVIDIA_API_KEY, or live inference. ## Related Issue Part of NVIDIA#2571 ## Changes - New `test/e2e-non-root-smoke.sh` (host-side bash, no `openshell`/`nemoclaw` CLI required): - **Test 1** — entrypoint setup chain completes cleanly under `--security-opt no-new-privileges` (regression guard for # 2472; passes a `true` command via the entrypoint's `NEMOCLAW_CMD` exec path so the gateway-launch branch is bypassed and we don't need the OpenShell-managed runtime). - **Test 2** — kernel confirms `NoNewPrivs=1` inside the container (defends the test itself against silent typos in the docker flag). - New job `test-non-root-sandbox-smoke` in `.github/workflows/pr-self-hosted.yaml` — `linux-amd64-cpu4`, `timeout-minutes: 5`, `needs: build-sandbox-images`, reuses the existing `isolation-image` artifact. - Expected results: ``` my-machine@ab1-cdf40-30:~/NemoClaw$ # Run script bash test/e2e-non-root-smoke.sh TEST: 1. Entrypoint setup chain completes under --security-opt no-new-privileges PASS: entrypoint exited 0 under no-new-privileges (NVIDIA#2472 setup chain healthy) TEST: 2. Kernel confirms NoNewPrivs=1 inside container (defends against silent flag typos) PASS: kernel confirms NoNewPrivs=1 ======================================== Results: 2 passed, 0 failed ======================================== ``` - Upcoming plans: - **Test 3** — `openclaw tui` does not error with "Missing gateway auth token" inside a login shell under the same constraint (regression guard for # 2482) after PR NVIDIA#2485 is merged ## Type of Change - [x] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [ ] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Verification <!-- Check each item you ran and confirmed. Leave unchecked items you skipped. Doc-only changes do not require npm test unless you ran it. --> - [ ] `npx prek run --all-files` passes - [ ] `npm test` passes - [ ] Tests added or updated for new or changed behavior - [ ] No secrets, API keys, or credentials committed - [ ] Docs updated for user-facing behavior changes - [ ] `make docs` builds without warnings (doc changes only) - [ ] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) --- <!-- DCO sign-off required by CI. Run: git config user.name && git config user.email --> Signed-off-by: Hung Le <hple@nvidia.com>
1 parent 0dbb995 commit 57470be

2 files changed

Lines changed: 120 additions & 0 deletions

File tree

.github/workflows/pr-self-hosted.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,23 @@ jobs:
160160

161161
- name: Run port override E2E tests
162162
run: NEMOCLAW_TEST_IMAGE=nemoclaw-production bash test/e2e-port-overrides.sh
163+
164+
test-non-root-sandbox-smoke:
165+
runs-on: linux-amd64-cpu4
166+
timeout-minutes: 5
167+
needs: build-sandbox-images
168+
steps:
169+
- name: Checkout
170+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
171+
172+
- name: Download image artifact
173+
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
174+
with:
175+
name: isolation-image
176+
path: /tmp
177+
178+
- name: Load image
179+
run: gunzip -c /tmp/isolation-image.tar.gz | docker load
180+
181+
- name: Run non-root sandbox smoke test
182+
run: NEMOCLAW_TEST_IMAGE=nemoclaw-production bash test/e2e-non-root-smoke.sh

test/e2e-non-root-smoke.sh

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#!/usr/bin/env bash
2+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
# E2E smoke test for non-root sandbox execution under
6+
# --security-opt no-new-privileges (issue #2571).
7+
#
8+
# Replicates Brev Launchable / DGX Spark's PR_SET_NO_NEW_PRIVS constraint
9+
# in CI to catch regressions of:
10+
#
11+
# - #2472 (2026-04-25): non-root entrypoint crashed when install_configure_guard
12+
# wrote to ~/.bashrc/.profile under Landlock + `set -e` — 5-day outage
13+
# invisible to CI. Architecturally gone after #2741 (rc files are now
14+
# root:root 444 static shims); this test guards the symptom — entrypoint
15+
# exits non-zero under no-new-privileges.
16+
#
17+
# (#2482-class detection — `openclaw tui` "Missing gateway auth token" — is
18+
# deferred to a follow-up PR after #2485 merges, since current main has no
19+
# token-generation code path in the standalone container.)
20+
#
21+
# CAVEAT: no-new-privileges ≠ Landlock. We catch #2472-class bugs only
22+
# when they manifest as a non-zero entrypoint exit; the original ~/.bashrc
23+
# write under Landlock is not reproduced. A real Landlock ruleset is future
24+
# work (#2571).
25+
#
26+
# How: ENTRYPOINT=`nemoclaw-start`, CMD=`["/bin/bash"]`. Passing a command
27+
# to `docker run` overrides CMD; the entrypoint captures it into NEMOCLAW_CMD
28+
# and exec's it *after* setup (nemoclaw-start.sh:1508 non-root / :1613 root).
29+
# We pass `true` (Test 1) so setup runs end-to-end without entering the
30+
# gateway-launch path (which needs OpenShell).
31+
#
32+
# Requires: docker
33+
34+
set -euo pipefail
35+
36+
IMAGE="${NEMOCLAW_TEST_IMAGE:-nemoclaw-production}"
37+
38+
RED='\033[0;31m'
39+
GREEN='\033[0;32m'
40+
YELLOW='\033[1;33m'
41+
NC='\033[0m'
42+
43+
pass() {
44+
echo -e "${GREEN}PASS${NC}: $1"
45+
PASSED=$((PASSED + 1))
46+
}
47+
fail() {
48+
echo -e "${RED}FAIL${NC}: $1"
49+
FAILED=$((FAILED + 1))
50+
}
51+
info() { echo -e "${YELLOW}TEST${NC}: $1"; }
52+
53+
PASSED=0
54+
FAILED=0
55+
56+
if ! docker image inspect "$IMAGE" >/dev/null 2>&1; then
57+
fail "Image $IMAGE not found — load it before running this test"
58+
exit 1
59+
fi
60+
61+
# Helper: run the entrypoint under --security-opt no-new-privileges with
62+
# a final command of the caller's choice. The command is captured by
63+
# nemoclaw-start as NEMOCLAW_CMD and exec'd after entrypoint setup.
64+
# Returns combined stdout+stderr; caller checks $? and/or output.
65+
run_under_nnp() {
66+
docker run --rm --security-opt no-new-privileges "$IMAGE" "$@" 2>&1 || return $?
67+
}
68+
69+
# ── Test 1: Entrypoint setup completes under no-new-privileges (#2472) ──
70+
71+
info "1. Entrypoint setup chain completes under --security-opt no-new-privileges"
72+
RC=0
73+
OUT=$(run_under_nnp true) || RC=$?
74+
if [ "$RC" -eq 0 ]; then
75+
pass "entrypoint exited 0 under no-new-privileges (#2472 setup chain healthy)"
76+
else
77+
fail "entrypoint exited $RC under no-new-privileges — likely #2472-class regression"
78+
echo "$OUT" | tail -20 | sed 's/^/ /'
79+
fi
80+
81+
# ── Test 2: Kernel confirms PR_SET_NO_NEW_PRIVS is applied (sanity) ──
82+
83+
info "2. Kernel confirms NoNewPrivs=1 inside container (defends against silent flag typos)"
84+
NNP=$(docker run --rm --security-opt no-new-privileges --entrypoint "" "$IMAGE" \
85+
sh -c 'grep ^NoNewPrivs /proc/self/status' 2>/dev/null \
86+
| awk '{print $2}' || echo "")
87+
if [ "$NNP" = "1" ]; then
88+
pass "kernel confirms NoNewPrivs=1"
89+
else
90+
fail "expected NoNewPrivs=1 inside container, got '${NNP:-<empty>}'"
91+
fi
92+
93+
# ── Summary ─────────────────────────────────────────────────────
94+
95+
echo ""
96+
echo -e "${GREEN}========================================${NC}"
97+
echo -e " Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
98+
echo -e "${GREEN}========================================${NC}"
99+
100+
[ "$FAILED" -eq 0 ] || exit 1

0 commit comments

Comments
 (0)