Skip to content

test(installer): root-secure the guarded-exec test fixtures so the suite runs as uid 0 (#2623) - #66

Merged
MichaelTaylor3d merged 3 commits into
mainfrom
claude/task-reminder-routine-45xjbj
Aug 11, 2026
Merged

test(installer): root-secure the guarded-exec test fixtures so the suite runs as uid 0 (#2623)#66
MichaelTaylor3d merged 3 commits into
mainfrom
claude/task-reminder-routine-45xjbj

Conversation

@MichaelTaylor3d

Copy link
Copy Markdown
Contributor

What & why

Closes DIG-Network/dig_ecosystem#2623. 10 lib tests fail when the suite runs as root (uid 0), green on the non-root CI runner — noisy for local/agent runs in root containers and a place real regressions could hide.

Root cause (single, shared — not "unprivileged-only asserts"): the root-exec guard (rootchain::verify, via secure::root_exec_guard / guardedcmd::GuardedCommand::for_installed_binary) walks every ancestor of a fixture dir up to / and refuses any level with group/other write. sources::fixture_root() defaults to /tmp (sticky 1777), so as root the guard correctly refuses fixtures there; on the non-root CI runner the guard is inert, so the tests pass — masking the breakage. (The fixture_root() doc already anticipated this via DIG_TEST_FIXTURE_ROOT.)

Fix — split (run for real under root), not skip

Added sources::root_secure_fixture_root(): honours DIG_TEST_FIXTURE_ROOT, else (as root) provisions a root-owned 0755 dir directly beneath / — whose only judged ancestor is / itself — and unprivileged returns exactly fixture_root(), so CI runs are byte-for-byte unchanged. Routed the 10 tests' fixture dirs through it. A world-writable leaf a test sets itself is still refused (only the incidental /tmp ancestor is removed). 10 split / 0 skipped → 0 coverage loss; every security assertion stays live and runs for real under root.

How verified

  • As root: cargo test --lib898 passed / 0 failed (was 888 / 10-failed).
  • Load-bearing: committed the fix, then neutered the helper to delegate straight to fixture_root() → reproduced exactly the same 10 failures; restored (tree clean).
  • cargo fmt --all -- --check clean; cargo clippy --all-targets -- -D warnings clean.

Blast radius

Test-only + behavior-preserving. No product code path changed — only test fixture locations and the new test helper. fixture_root() (50 sites/16 files) is untouched; only the 10 root-guard-PASS tests route through the new helper. No non-root assertion weakened.

Version

0.42.3 → 0.42.4 (patch — test infra, behavior-preserving). Rides the nightly (apps §3.6-A). DEVELOPMENT_LOG.md records the root-secure-fixture convention; gui/app/src-tauri/Cargo.lock re-pinned dig-installer 0.42.4.

Closes DIG-Network/dig_ecosystem#2623


Generated by Claude Code

claude added 2 commits August 11, 2026 03:01
…ite runs as uid 0 (#2623)

Fix the fixture LOCATION, never the verify: add `sources::root_secure_fixture_root()`,
which honours `DIG_TEST_FIXTURE_ROOT` (the baked-container path) and otherwise, as
root, provisions a root-owned `0755` directory directly beneath `/` (whose only judged
ancestor is `/` itself). Unprivileged it is exactly `fixture_root()`, so the CI runs are
byte-for-byte unchanged. A world-writable leaf a test sets itself is still refused; only
the incidental `/tmp` ancestor is removed — so all ten tests now run FOR REAL as root
(no skips, no coverage loss) and every security assertion stays live.

Tests routed through the new helper (all previously root-only-failing):
- secure::root_refuses_to_exec_a_binary_from_a_group_writable_dir (positive control arm)
- guardedcmd::a_refused_directory_yields_no_command_at_all (positive control arm)
- dns::doctor 7 spawn/poll tests (via tmp_subdir + the missing-binary case)
- dns::verify_existing_reports_installed_true_and_live_when_the_service_answers (via tmp_subdir)

Version: PATCH 0.42.3 -> 0.42.4 (test-only, behaviour-preserving); gui Cargo.lock re-pinned.

Verified as root: cargo test --lib -> 898 passed / 0 failed (was 888/10-failed);
cargo fmt --all --check clean; cargo clippy --all-targets -D warnings clean.

Co-Authored-By: Claude <noreply@anthropic.com>
… unrelated relock) (#2623)

The gui lock sync ran without --offline and picked up unrelated transitive bumps (windows-sys 0.60->0.61, getrandom 0.3->0.4). Restore the gui lock from main and re-pin ONLY dig-installer 0.42.3->0.42.4, keeping this test-only PR's diff scoped.

Co-Authored-By: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Contributor Author

Correctness gate — PASS (loop-reviewer); non-gating findings addressed

Test-only + behavior-preserving change → single correctness gate (§1.10).

loop-reviewer — PASS, verified empirically (not trusted from the maker): cargo test --lib898 passed / 0 failed under root; the RED proof genuinely reproduces exactly the 10 failures when root_secure_fixture_root() is neutered to delegate to fixture_root(), and all 898 pass with it intact (the helper does real work). The helper: non-root returns exactly fixture_root() (CI byte-identical); as root creates a root:root 0755 dir under / whose only judged ancestor is /, uniquely leafed per {tag}-{pid} (no cross-test/process collision), loud-.expect() (never silently routes to a writable-ancestor path), name unmistakably a test artifact. Both guard-refusal arms still bite (a world-writable leaf is still refused) — no security assertion weakened; tests are split (run for real), not skipped, so no coverage loss.

Non-gating findings:

  1. gui Cargo.lock scope creep — FIXED in 4b31479. The lock sync had ridden along unrelated transitive bumps (windows-sys 0.60→0.61, getrandom 0.3→0.4) because it ran without --offline. Restored the gui lock from main and re-pinned only dig-installer 0.42.3→0.42.4, so this test-only PR's diff is now scoped (verified: gui lock diff vs main is the single version line; --locked build proceeds past the lock check).
  2. root-secure base dir left under / after a run — accepted as-is: harmless in the ephemeral root CI container, and adding per-test cleanup to 10 sites is more churn than value. Noted for the follow-up below.

Follow-up (Low, to file post-merge): fold the self-provisioning into fixture_root() (or container setup) so the ~23 historically-root-affected tests never need per-test routing, and add base-dir cleanup — the reviewer's suggested consolidation.

The 0ac2a3d..4b31479 delta is lock-trim-only (removes changes, no code) — the PASS stands. Merging on all required checks green + zero threads.


Generated by Claude Code

@MichaelTaylor3d
MichaelTaylor3d merged commit 6bf7705 into main Aug 11, 2026
31 checks passed
@MichaelTaylor3d
MichaelTaylor3d deleted the claude/task-reminder-routine-45xjbj branch August 11, 2026 03:33
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.

2 participants