Skip to content

test(e2e): add sandbox security & durability e2e cases - #1069

Open
G4614 wants to merge 5 commits into
boxlite-ai:mainfrom
G4614:test/sandbox-security-e2e
Open

test(e2e): add sandbox security & durability e2e cases#1069
G4614 wants to merge 5 commits into
boxlite-ai:mainfrom
G4614:test/sandbox-security-e2e

Conversation

@G4614

@G4614 G4614 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Add four sandbox platform e2e cases covering security & durability P0s the suite was missing: outbound allow-list enforcement, secret/access-token non-leak into the guest, cross-box filesystem isolation, and on-box data (file + WAL SQLite) survival across stop/start.

Test plan:

  • ruff format --check + ruff check on the four new cases (clean)
  • python -m py_compile on all four (syntax OK)
  • Written against the live SDK surface (NetworkSpec/Secret/BoxOptions, box.start()/stop())
  • Full run under make test:e2e — requires the gated self-hosted e2e runner; not run here. Two assertions may legitimately surface real gaps rather than test bugs: per-IP allow-list enforcement (test_egress_allowlist) and rootfs survival across the managed runner's stop/start (test_restart_persistence).

Summary by CodeRabbit

  • Tests
    • Added a new end-to-end P0 filesystem isolation test ensuring data written in one sandbox isn’t readable or discoverable from another.
    • Expanded network egress allowlist testing with UDP coverage (verifying “disabled” blocks all UDP, and allowlist permits only the specified resolver).
    • Added an end-to-end restart persistence test confirming both a marker file and SQLite WAL data survive stop/start.
    • Added an end-to-end secret hygiene test ensuring secret values and API tokens never appear inside the sandbox.
    • Added an end-to-end export/import roundtrip test verifying payload integrity into a newly imported sandbox.
    • Improved sandbox cleanup reliability after test runs.

Cover four platform P0s that the suite was missing: outbound allow-list
enforcement, secret/token non-leak into the guest, cross-box filesystem
isolation, and on-box data (file + SQLite) survival across stop/start.
@boxlite-agent

boxlite-agent Bot commented Jul 28, 2026

Copy link
Copy Markdown

📦 BoxLite review — looks good · f6fbb8d

Review evidence

  • git diff --numstat / git diff origin/main...HEAD — 5 new e2e test files, 521 lines, no prod code touched
  • python3 -m py_compile on all 5 new test files — all compile cleanly
  • grep API signatures in sdks/python/src/{options,runtime,box_handle}.rs — NetworkSpec/Secret/import_box/export/stop/start match test usage
  • pytest run of new e2e cases — requires live runner+microVM+network; unavailable in this sandbox

Risk notes

  • isolation test — grep excludes proc/sys/dev/run/tmp with timeout guard; DONE sentinel check is sound
  • egress allow_net — TCP+UDP probes use raw IPs, no DNS dependency; UDP case documents pre-fix(gvproxy)!: apply allow_net to UDP egress #1090 gap correctly
  • secret non-leak — dumps env/proc1/disk paths; token-leak assertion is trivially true since API token is never wired into guest by design, weak but not incorrect
  • export/import roundtrip — exact string compare with printf %s (no trailing newline) avoids flaky whitespace bugs; cleanup handles partial failure
  • restart persistence — WAL checkpoint-before-close scoping is explicit and correct; doesn't overclaim un-checkpointed WAL durability
  • coverage — conftest.py and lib/e2e_auth.py, path_verification.py only sampled for fixture/API contract confirmation, not full review
scripts/test/e2e/cases/test_cross_box_isolation.py
  test_box_b_cannot_read_box_a_filesystem  +70/-0  new negative isolation test
scripts/test/e2e/cases/test_egress_allowlist.py
  test_network_disabled_blocks_all_egress / allowlist / udp variants  +194/-0  new TCP+UDP allow_net tests
scripts/test/e2e/cases/test_export_import_roundtrip.py
  test_data_survives_export_import_to_new_box  +72/-0  new export/import data test
scripts/test/e2e/cases/test_restart_persistence.py
  test_file_and_sqlite_survive_stop_start  +91/-0  new stop/start durability test
scripts/test/e2e/cases/test_secret_no_leak.py
  test_secret_value_and_api_token_never_visible_in_guest  +94/-0  new secret non-leak test

reviewed f6fbb8d in a BoxLite microVM · @boxlite-agent review to re-run · powered by BoxLite

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds five async E2E test modules covering cross-box filesystem isolation, TCP and UDP egress filtering, filesystem and SQLite persistence across restart, guest secret/API-token visibility, and export-import data durability.

Changes

Sandbox guarantees

Layer / File(s) Summary
Cross-box filesystem isolation
scripts/test/e2e/cases/test_cross_box_isolation.py
Creates two boxes, writes a marker in box A, and verifies box B cannot read or discover it.
Network egress filtering
scripts/test/e2e/cases/test_egress_allowlist.py
Uses in-guest TCP and UDP probes to validate disabled networking and allowlisted versus blocked IPv4 destinations.
Restart filesystem persistence
scripts/test/e2e/cases/test_restart_persistence.py
Writes a file and committed SQLite row, restarts the same box, and verifies both values persist.
Guest secret visibility
scripts/test/e2e/cases/test_secret_no_leak.py
Inspects environment, process, and disk surfaces to verify the configured secret and API token are not exposed.
Export-import data roundtrip
scripts/test/e2e/cases/test_export_import_roundtrip.py
Exports filesystem data from one box, imports it into a distinct box, and verifies the payload is preserved.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • boxlite-ai/boxlite#1090: Applies allow_net behavior to UDP egress, which is directly exercised by the new UDP allowlist tests.

Suggested reviewers: dorianzheng

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: new sandbox security and durability e2e cases.
Description check ✅ Passed The description covers the main summary and verification steps, though it uses a test plan section instead of the template headings.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@boxlite-agent boxlite-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📦 BoxLite review — 1 issue

Comment thread scripts/test/e2e/cases/test_cross_box_isolation.py Outdated
Comment thread scripts/test/e2e/cases/test_secret_no_leak.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/test/e2e/cases/test_cross_box_isolation.py`:
- Around line 56-62: Update the assertions in the _run call within the cross-box
isolation test to require output.strip() to equal exactly "DONE", ensuring grep
returned no matching paths while preserving the existing isolation-breach
diagnostic context.

In `@scripts/test/e2e/cases/test_restart_persistence.py`:
- Around line 26-35: Update the `_WRITE` subprocess script to keep the SQLite
connection open or exit via `NO_CKPT_ON_CLOSE`/`_exit` after printing `WROTE`,
preventing WAL checkpointing and deletion before `stop()`. Add an assertion that
the WAL file exists before restarting, so the test specifically validates
WAL-only persistence.

In `@scripts/test/e2e/cases/test_secret_no_leak.py`:
- Around line 41-47: Update the generated secret-scanning script in
test_secret_no_leak.py to recursively traverse directory paths under
/etc/environment, /root/.boxlite, and /run/secrets, including files such as
/run/secrets/e2e_probe in the collected buffer. Preserve the existing
file-reading and exception-tolerant behavior while ensuring directory contents
are inspected rather than opened only as directory entries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f68b0b46-b62f-48af-8dbe-e010c5b682a6

📥 Commits

Reviewing files that changed from the base of the PR and between e3563b3 and ef2d0be.

📒 Files selected for processing (4)
  • scripts/test/e2e/cases/test_cross_box_isolation.py
  • scripts/test/e2e/cases/test_egress_allowlist.py
  • scripts/test/e2e/cases/test_restart_persistence.py
  • scripts/test/e2e/cases/test_secret_no_leak.py

Comment thread scripts/test/e2e/cases/test_cross_box_isolation.py Outdated
Comment thread scripts/test/e2e/cases/test_restart_persistence.py
Comment thread scripts/test/e2e/cases/test_secret_no_leak.py Outdated

@boxlite-agent boxlite-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📦 BoxLite review — 1 issue

Comment thread scripts/test/e2e/cases/test_cross_box_isolation.py

@boxlite-agent boxlite-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📦 BoxLite review — 1 issue

Comment thread scripts/test/e2e/cases/test_export_import_roundtrip.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/test/e2e/cases/test_export_import_roundtrip.py`:
- Around line 65-67: Update the cleanup logic around the source and destination
removals so rt.remove(dst.id, force=True) is attempted even when removing src.id
raises an exception. Use nested finally blocks or independent cleanup handling,
while preserving the conditional check that skips destination cleanup when dst
is None.
- Around line 61-63: Update the assertion in the export/import round-trip test
to compare the captured output directly with SENTINEL, removing the strip() call
so leading and trailing whitespace changes are detected while preserving the
existing failure message.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f8871f53-fc8e-4159-9e84-579aa6b7a664

📥 Commits

Reviewing files that changed from the base of the PR and between 1e683e9 and ca076c0.

📒 Files selected for processing (1)
  • scripts/test/e2e/cases/test_export_import_roundtrip.py

Comment thread scripts/test/e2e/cases/test_export_import_roundtrip.py Outdated
Comment thread scripts/test/e2e/cases/test_export_import_roundtrip.py Outdated
@G4614
G4614 requested a review from a team as a code owner July 30, 2026 10:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/test/e2e/cases/test_cross_box_isolation.py`:
- Around line 62-64: Update the command constructed in the cross-box isolation
test to preserve and inspect the grep/timeout exit status instead of
unconditionally echoing DONE. Treat only grep’s no-match status as successful,
while propagating timeout, unavailable-command, and other scan failures; retain
the existing marker detection behavior and avoid hiding actionable errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 30139c76-7ccf-43bb-a0fe-0db10b0080eb

📥 Commits

Reviewing files that changed from the base of the PR and between ca076c0 and 7da8ed3.

📒 Files selected for processing (2)
  • scripts/test/e2e/cases/test_cross_box_isolation.py
  • scripts/test/e2e/cases/test_export_import_roundtrip.py

Comment thread scripts/test/e2e/cases/test_cross_box_isolation.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
scripts/test/e2e/cases/test_egress_allowlist.py (2)

77-100: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Prove blocked-target reachability before asserting filtering.

Both tests can pass because BLOCKED_IP is unavailable, not because egress filtering works.

  • scripts/test/e2e/cases/test_egress_allowlist.py#L77-L100: preflight BLOCKED_IP over unrestricted TCP or use a controlled responder.
  • scripts/test/e2e/cases/test_egress_allowlist.py#L185-L192: preflight BLOCKED_IP over unrestricted UDP or use a controlled responder.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/test/e2e/cases/test_egress_allowlist.py` around lines 77 - 100,
Ensure BLOCKED_IP reachability is established before validating filtering: in
scripts/test/e2e/cases/test_egress_allowlist.py lines 77-100, preflight the
target over unrestricted TCP or use a controlled responder before asserting
test_allowlist_permits_only_listed_host; apply the corresponding unrestricted
UDP preflight or controlled responder in lines 185-192. Keep the existing
assertions and cleanup behavior, but make both tests fail setup when BLOCKED_IP
is inherently unavailable rather than treating that as filtering evidence.

44-55: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound process waiting and stream draining together.

Both helpers drain output before starting the process timeout, so a stalled stream can hang the E2E suite indefinitely.

  • scripts/test/e2e/cases/test_egress_allowlist.py#L44-L55: run drain(ex) concurrently with the bounded ex.wait().
  • scripts/test/e2e/cases/test_egress_allowlist.py#L140-L141: apply the same timeout-safe sequencing to the UDP helper.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/test/e2e/cases/test_egress_allowlist.py` around lines 44 - 55, Update
_probe and the UDP helper at scripts/test/e2e/cases/test_egress_allowlist.py
lines 44-55 and 140-141 so drain(ex) runs concurrently with the bounded
ex.wait() call, ensuring both stream consumption and process completion are
covered by the timeout; preserve the existing output, exit-code, and result
validation behavior at both sites.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@scripts/test/e2e/cases/test_egress_allowlist.py`:
- Around line 77-100: Ensure BLOCKED_IP reachability is established before
validating filtering: in scripts/test/e2e/cases/test_egress_allowlist.py lines
77-100, preflight the target over unrestricted TCP or use a controlled responder
before asserting test_allowlist_permits_only_listed_host; apply the
corresponding unrestricted UDP preflight or controlled responder in lines
185-192. Keep the existing assertions and cleanup behavior, but make both tests
fail setup when BLOCKED_IP is inherently unavailable rather than treating that
as filtering evidence.
- Around line 44-55: Update _probe and the UDP helper at
scripts/test/e2e/cases/test_egress_allowlist.py lines 44-55 and 140-141 so
drain(ex) runs concurrently with the bounded ex.wait() call, ensuring both
stream consumption and process completion are covered by the timeout; preserve
the existing output, exit-code, and result validation behavior at both sites.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b56b533-a945-44bd-87e5-5cf7cae5e6d1

📥 Commits

Reviewing files that changed from the base of the PR and between 7da8ed3 and f6fbb8d.

📒 Files selected for processing (1)
  • scripts/test/e2e/cases/test_egress_allowlist.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant