8000 fix(minimal): accept a sticky root-owned directory and check before changing the host by norrietaylor · Pull Request #2195 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

fix(minimal): accept a sticky root-owned directory and check before changing the host - #2195

Merged
norrietaylor merged 2 commits into
release/0.7.0from
fix/finalize-install-dir-check-0.7.0
Oct 13, 2026
Merged

norrietaylor merged 2 commits into
release/0.7.0from
fix/finalize-install-dir-check-0.7.0

Conversation

@norrietaylor
@norrietaylor norrietaylor commented Oct 13, 2026 •
Copy link
Copy Markdown
Member

Problem

On a Mac whose /Library/PrivilegedHelperTools carries the sticky bit, min finalize-install stops with:

/Library/PrivilegedHelperTools … is not a root-owned, non-sticky directory closed to group and other writes; Minimal's DNS service was not installed

Two defects are behind that report.

  1. The sticky bit is refused for no reason. The directory is root-owned and closed to group and other writes, so it is safe. The sticky bit only narrows who may rename or remove an entry; it grants nobody anything. Some macOS hosts set it on this directory.
  2. A refusal leaves the host half-configured. For a bare program the step wrote /etc/resolver/min.internal and loaded the local-range unit before it ran the directory check. A refused install left both behind, with no service to answer.

Change

  • root_dir_steps no longer tests the sticky bit. It still requires root ownership and no group or other write, on the directory and on every ancestor up to /.
  • macos_names_steps runs the service program's verified copy first, for a bare program and for an app bundle. The copy holds every check that can refuse (directory custody, hash pin, Developer ID re-check), so a refusal leaves no resolver file and no range unit behind.
  • linux_names_steps does the same. The Linux answerer steps are split into a copy half and a unit half, as on macOS: the copy runs first, then the resolver link, then the units.
  • The refusal message drops "non-sticky".
  • docs/internal/release-pipeline.md describes the check as it now is.

Tests

  • privileged_copy_refuses_a_user_writable_ancestor now accepts mode 1755 and refuses 1777.
  • New copy_runs_before_anything_changes_the_host asserts the directory check precedes every host change: the resolver file and the range unit on macOS (bare and bundle form), the resolver link and the unit files on Linux.
  • advisory_command_reserves_the_range_on_macos, linux_advisory_command_is_unchanged and finalize_install_show_script_is_an_annotated_posix_script are updated to the new step order.

Verification

Run from macOS by cross-compiling to musl in Docker:

  • cross clippy -p minimal --all-targets -- -D warnings: clean.
  • cross test -p minimal: 438 passed, 2 failed. Every resolver test passes. The two failures are in crates/minimal/src/tests.rs, which this PR does not touch: a_stopped_vm_does_not_hold_a_box_name_resolution (a timing bound, 1.3–1.6 s under emulation) and legacy_network_spellings_parse_with_hint (stderr capture). I did not run them on the base commit to confirm they fail there too.
  • just lint-prose docs/internal/release-pipeline.md: 0 alerts.

Not run: just e2e, just test-vm, the real privileged step on a Mac with a sticky /Library/PrivilegedHelperTools, and the real privileged step on a Linux host.

Needs a forward-port to main.

Summary by CodeRabbit

  • Bug Fixes
    • macOS and Linux installations now complete the verified answerer-copy step before changing resolver settings. On macOS, the range unit is installed after resolver setup and before the answerer unit.
    • Root-owned directories with the sticky bit are accepted when they are not writable by group or others. Directories that are not root-owned or are writable by group or others remain restricted.

…hanging the host

min finalize-install refused to install the DNS service on a Mac whose
/Library/PrivilegedHelperTools carries the sticky bit, and left the resolver
file and the local range behind when it refused.

The directory check no longer tests the sticky bit. On a root-owned directory
closed to group and other writes the bit grants nobody anything: it only
narrows who may rename or remove an entry. Ownership and the write bits are
still checked on the directory and every ancestor.

The macOS step now runs the service program's verified copy first, for a bare
program and for an app bundle alike. The copy holds every check that can
refuse, so a refusal leaves no resolver file and no range unit behind.
@norrietaylor
norrietaylor requested a review from a team as a code owner October 13, 2026 16:47
@coderabbitai
coderabbitai Bot commented Oct 13, 2026 •
Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: cb53cb4f-16ed-4fe8-b96e-8087fcaea18f
📥 Commits

Reviewing files that changed from the base of the PR and between a42790f and 83c1c05.

📒 Files selected for processing (1)
  • crates/minimal/src/resolver.rs

Included review availability: This review used your included allowance. Your plan provides up to 5 included reviews per hour; 0 remain after this review.

📝 Walkthrough

macOS and Linux install scripts now run verified answerer copying before resolver changes and install answerer units afterward. macOS installs the range unit before the answerer unit. Directory custody checks no longer reject the sticky bit, but still require root ownership and prohibit group or other write access.

Changes: Answerer Setup and Directory Custody

Layer / File(s) Summary
Answerer copy and unit ordering
crates/minimal/src/resolver.rs
macOS and Linux install steps run verified answerer copying before resolver changes. macOS installs the range unit before the answerer unit. Linux installs the answerer unit after resolver setup. Tests verify the step order.
Directory custody criteria
crates/minimal/src/resolver.rs, docs/internal/release-pipeline.md
Directory checks still require root ownership and reject group or other write access. They no longer reject the sticky bit. Tests and documentation reflect the updated criteria.

Priority: ⬇️ Low

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

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant InstallSteps
  participant AnswererCopy
  participant ResolverSetup
  participant UnitSetup
  InstallSteps->>AnswererCopy: Run verified copy checks
  InstallSteps->>ResolverSetup: Apply resolver changes
  ResolverSetup->>UnitSetup: Install answerer unit
Loading
Merge Risk: ⚪ Minimal · up to 83c1c

No merge-blocking issue is established. Complete normal validation and the planned forward-port before release.

Pre-merge checks · Passed 5
Passed Passed checks (5 passed)
Check name Status Explanation
Title check Passed The title clearly describes both primary changes: accepting sticky root-owned directories and performing checks before host changes. It uses a concise Conventional Commit format.
Description check Passed The description explains the problem, implementation changes, tests, verification results, known failures, and unrun checks. It is mostly complete, although it does not use the template's exact Summar…
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 15 functions across 1 files.
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 docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

I’m a rabbit checking paths in the night
Root-owned and closed means the permissions are right
Sticky bits pass; the checks carry on
Copy comes first, then resolver is drawn
Units follow, and I hop toward the dawn

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

@minimal-foundry
minimal-foundry Bot commented Oct 13, 2026 •
Copy link
Copy Markdown
Contributor

Approved: no findings at 83c1c05cb6ea.

Nothing to do.

Clobber: none: git log on resolver.rs shows the two PR commits on top of the bundle-packaging change; nothing reverts a recent fix.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minimal-review — approved

Verdict APPROVE — 1 finding, 1 inline.

Comment thread crates/minimal/src/resolver.rs Resolved
@minimal-foundry minimal-foundry Bot added the review:approved minimal-review approved the current head label Oct 13, 2026
The Linux step had the same ordering as the macOS one: it made the resolver
link before the service program's verified copy, so a refused copy left the
link behind with no service to answer.

The answerer steps are split into a copy half and a unit half, as on macOS.
The copy runs first, then the resolver link, then the units.
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minimal-review — approved

Verdict APPROVE — No findings.

@norrietaylor
norrietaylor merged commit eefd8ef into release/0.7.0 Oct 13, 2026
32 checks passed
@norrietaylor
norrietaylor deleted the fix/finalize-install-dir-check-0.7.0 branch October 13, 2026 19:05
norrietaylor added a commit that referenced this pull request Oct 15, 2026
…hanging the host (#2195)

* fix(minimal): accept a sticky root-owned directory and check before changing the host

min finalize-install refused to install the DNS service on a Mac whose
/Library/PrivilegedHelperTools carries the sticky bit, and left the resolver
file and the local range behind when it refused.

The directory check no longer tests the sticky bit. On a root-owned directory
closed to group and other writes the bit grants nobody anything: it only
narrows who may rename or remove an entry. Ownership and the write bits are
still checked on the directory and every ancestor.

The macOS step now runs the service program's verified copy first, for a bare
program and for an app bundle alike. The copy holds every check that can
refuse, so a refusal leaves no resolver file and no range unit behind.

* fix(minimal): run the linux verified copy before the resolver link

The Linux step had the same ordering as the macOS one: it made the resolver
link before the service program's verified copy, so a refused copy left the
link behind with no service to answer.

The answerer steps are split into a copy half and a unit half, as on macOS.
The copy runs first, then the resolver link, then the units.

(cherry picked from commit eefd8ef)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review:approved minimal-review approved the current head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0