ROCMAI-83: extract sandbox.rs from apps/rocmd/src/lib.rs - #483
jussielo-amd wants to merge 1 commit into
Conversation
r0x0r
left a comment
There was a problem hiding this comment.
Review (draft): the sandbox isolation is intact; one doc line to fix
Reviewed as a draft, so this is a comment rather than an approval — mark it ready and I'll re-review. The substance is in good shape: I treated the bubblewrap and policy-gate code as the real risk in this diff and checked it flag by flag rather than relying on the test count.
Security-critical motion: verified identical
This is the part of Phase 5 where a reordered line would be a sandbox escape rather than a compile error, so stating the result explicitly:
- Bubblewrap argument construction matches
lib.rs:467–510onmainexactly, flags and order both:--die-with-parent,--new-session,--unshare-ipc,--unshare-uts,--proc /proc,--dev /dev,--tmpfs /tmp,--tmpfs /run, then the conditional--unshare-net. The network-unshare conditionif !(matches!(tool, SandboxToolArg::PrefetchArtifact) && policy.allow_artifact_download)is unchanged, as is the bind sequence (ro-binds for system dirs / exe_dir / config_dir, conditional bind vs ro-bind for data_dir and cache_dir). - Native fallback — the
allow_native_fallbackgate is unchanged; no new path skips the sandbox. - Prefetch policy gate —
allow_artifact_download,artifact_max_bytesandallow_huggingface_downloadare byte-identical, and no default flipped from deny to allow.huggingface_tokenis still only ever passed as a bearer header; it is not logged, not folded into an error message, and not reachable through captured stdout/stderr.resolve_huggingface_tokencorrectly stays inlib.rswithSandboxToolPolicy::from_cli. - HuggingFace URL classification —
is_huggingface_urlandhttp_url_hostunchanged, including thehuggingface.co/.huggingface.co/hf.co/.hf.cohost matching. - Atomic writes —
write_file_atomically_with_publishpreserves its ordering exactly: reserve temp →write_all→drop(file)→before_publish()→publish(&tmp, path).inspect_err(|_| fs::remove_file(&tmp)). Cleanup on both write failure and publish failure survives, and thepublish_temp_file/replace_file_windowssplit is identical.
The doc hunk describes a state this branch is not in
docs/architecture.md now claims five extracted modules; this branch has one. persistence.rs, common.rs, webhook.rs and cli.rs are all absent, and the items they are documented as owning — load_managed_services, CommandCapture, SandboxToolArg/SandboxToolPolicy — are still in lib.rs here.
Across the phase this is a pattern rather than an isolated slip, and the cross-PR shape is not visible from inside any single one:
- All five PRs rewrite the same single line, each branched independently from
main, so four are guaranteed to conflict on it. - Each text is a cumulative prefix that is only true at merge order 477 → 479 → 480 → 481 → 483.
- The quiet failure is a PR other than #477 merging first: nothing to conflict with, applies cleanly, and
mainends up asserting files that do not exist. Being last in the sequence, this PR carries the largest such claim.
Scoping the hunk to sandbox.rs alone and letting each sibling append its own module as it lands fixes it at any merge order and reduces the conflicts to trivial appends. Suggested replacement for this branch:
lib.rsmodularization is in progress (ROCMAI-83, Phase 5 of EAI-7768's sequencing). Extracted so far:sandbox.rs(bubblewrap/native sandbox execution, atomic-write helpers, artifact prefetch policy gating, and the sandbox-tool result shaping forcheck_updates/driver_plan).
Verified clean
- Visibility of the seven crate-root back-references —
CommandCapture(including its private fieldsargv/exit_status/stdout/stderr),SandboxToolArg,SandboxToolPolicy,ARTIFACT_PREFETCH_TIMEOUT,load_managed_services,restart_managed_service,stop_managed_serviceandrun_rocm_capture_for_pathsare all private inlib.rs, which is fine:mod sandbox;makes this a direct child of the crate root, and private items are visible to descendant modules. Both the production reads insandbox_check_updates_value/sandbox_driver_plan_valueand the test struct literals work. - Call sites — all six
pub(crate)functions insandbox.rsare calledsandbox::-qualified fromlib.rs; none missed. - Imports —
sha2::{Digest, Sha256}correctly dropped fromlib.rs(no reference survives) and re-added tosandbox.rsunder#[cfg(test)], consistent withsha256_hexitself being test-only.OsStrcorrectly followedtemp_sibling_pathacross. Nothing fails under-D warnings. - Tests — 130 attributes conserved across the branch (93 + 37), matching the claimed 37 exactly, and
write_file_atomically_cleans_up_temp_on_write_failurekept its#[ignore = "fills /dev/shm to provoke ENOSPC; not safe to run concurrently"]. That attribute surviving a 16.9k-line move is easy to lose and would have turned a deliberately-skipped destructive test into one that runs in CI. The tests left inlib.rsdo usesandbox_check_updates_value/sandbox_driver_plan_valueonly as mock runner results feedinghandle_therock_update_event_with_runner, so the split rationale holds. - No TODOs, commented-out code or half-moved clusters — nothing in the diff looks unfinished despite the draft status.
- Blast radius — nothing outside
apps/rocmd/src/references the moved symbols; no e2e scenario touches them. - Leak scan (AGENTS.md §2) — the one hit is the pre-existing phrase "restricted internal tool API" in an error string, moved verbatim. Clean.
Non-blocking: test-helper duplication, third copy
temp_app_paths, unique_test_root and workspace_test_artifact_dir are duplicated byte-identically into sandbox.rs while remaining in lib.rs. This is the third copy in the phase (#477 and #479 make the others) and Phase 5 has seven target modules. A single #[cfg(test)] mod test_support; at the crate root, added once, would stop this at three rather than eight — each copy being independently able to drift the next time the artifact path changes.
🤖 by agent-hub on AMD AgentHub
Fifth PR of Phase 5 (rocmd modularization, ROCMAI-27): pull bubblewrap/ native sandbox execution, the atomic-write helper family, artifact prefetch policy gating, and the check_updates/driver_plan sandbox-tool result shaping into their own module. No behavior change. Rebased onto the common.rs/cli.rs/persistence.rs extractions earlier in this stack, so this module now reaches those items through their real owners (common::CommandCapture, cli::SandboxToolArg/SandboxToolPolicy, persistence::load_managed_services) instead of a crate-root placeholder; restart_managed_service, run_rocm_capture_for_paths, and stop_managed_service stay at the crate root pending service.rs. The rebase also found update_check_message independently duplicated in common.rs (landed earlier in this stack) and sandbox.rs (this PR); sandbox.rs now calls common's copy instead of keeping its own, and cli.rs's two sandbox-dispatch call sites (run_sandbox_runner, run_sandbox_tool) were updated to the sandbox:: path. 37 tests that exercise this module's own logic (atomic-write semantics, sandbox-tool dispatch and output shaping, prefetch policy gating, huggingface-URL classification) moved into sandbox.rs's own #[cfg(test)] mod in this same PR. A handful of interleaved tests that are really about the managed-service-registry stop/restart lifecycle (service.rs), or that use this module's sandbox_check_updates_value/ sandbox_driver_plan_value purely as mock watcher-event data, stayed in lib.rs to move with service.rs/watchers.rs in their own later PRs. sandbox.rs's test module now reuses crate::test_support::temp_app_paths (the shared fixture introduced earlier in this stack) instead of its own copy of temp_app_paths/unique_test_root/workspace_test_artifact_dir. Signed-off-by: Jussi Elo <jussi.elo@amd.com>
aef762d to
e92b913
Compare
|
Thanks for the thorough review. Both points addressed in e92b913 (rebased onto #481 /
Since the rebase lands this on top of |
Summary
Fifth PR of Phase 5 (ROCMAI-83, part of the modularization epic ROCMAI-27). Pulls bubblewrap/native sandbox execution, the atomic-write helper family, artifact prefetch policy gating, and the
check_updates/driver_plansandbox-tool result shaping into their ownsandbox.rsmodule.CommandCapture,SandboxToolArg/SandboxToolPolicy,load_managed_services,restart_managed_service,stop_managed_service,run_rocm_capture_for_paths) viacrate::, since the modules that will eventually own those (common.rs/cli.rs/persistence.rs/watchers.rs/mcp.rs/service.rs) are separate, independent PRs not present on this branch (see ROCMAI-83: extract persistence.rs from apps/rocmd/src/lib.rs #477, ROCMAI-83: extract common.rs from apps/rocmd/src/lib.rs #479, ROCMAI-83: extract webhook.rs from apps/rocmd/src/lib.rs #480, ROCMAI-83: extract cli.rs from apps/rocmd/src/lib.rs #481).sandbox.rs's own#[cfg(test)] modin this same PR.sandbox_check_updates_value/sandbox_driver_plan_valuepurely as mock watcher-event data, stayed inlib.rsto move withservice.rs/watchers.rsin their own later PRs.docs/architecture.mdupdated in this PR.Independent of #477, #479, #480, #481 — branched separately from
main, not stacked, per the one-off PR approach for this phase.Test plan
cargo build(rocmd, androcm+rocmdtogether to confirm the external API surface is untouched)cargo clippy --workspace --all-targets -- -D warningscargo test -p rocmd— 130 tests, same count as before this change (129 passed + 1 pre-existing ignored)cargo xtask manifest --checkcargo fmt/ prek hooks cleantests/e2e-cucumber(CI)