Skip to content

fix(http): make raw executables readable, not just executable - #11135

Merged
jdx merged 3 commits into
mainfrom
fix/http-raw-file-readable
Jul 20, 2026
Merged

jdx merged 3 commits into
mainfrom
fix/http-raw-file-readable

Conversation

@jdx

@jdx jdx commented Jul 20, 2026 •

Copy link
Copy Markdown
Owner

Problem

Reported in #11108. When a raw executable (e.g. a PHP PHAR like Composer) is installed via the HTTP backend, mise creates it with mode 0711 instead of 0755.

file::make_executable only added the execute bits:

perms.set_mode(perms.mode() | 0o111);

The downloaded tempfile is created with restrictive perms (0600), so after +x it becomes 0711 — executable by everyone but readable only by the owner. For system-wide installs owned by root, a non-owner user can execute the file but the interpreter (PHP) can't read the PHAR, so:

$ composer --version
Could not open input file: /usr/local/bin/composer

This affects both the raw-file and decompressed-raw-file paths in src/backend/http.rs.

Fix

Add the matching read bits alongside the execute bits, so any class that gains execute also gains read (mode | 0o111 | 0o444). Existing write bits are preserved; an already-0755 file is unchanged.

This is applied in the shared make_executable / make_executable_async helpers, so it hardens the invariant "executable ⇒ readable" everywhere, not just the HTTP backend.

Test

Added a unit test covering the mode transformation (0o600 → 0o755, 0o640 → 0o755, 0o660 → 0o775, 0o755 unchanged).

Fixes #11108


Note

Low Risk
Small, localized Unix permission logic with unit tests; broad call sites only widen read on paths already being marked executable.

Overview
Fixes interpreted executables (e.g. PHP PHARs from the HTTP backend) that were left executable but not world-readable when installs started from restrictive temp modes like 0600 (0711 after chmod +x).

On Unix, make_executable and make_executable_async now set modes via a shared executable_mode helper that applies mode | 0o111 | 0o444, so any class that gains execute also gains the matching read bits while preserving existing write bits; modes that are already correct (e.g. 0755) stay unchanged.

Adds a test_executable_mode_adds_read_with_execute unit test for the mode mapping (600/640 → 755, 660 → 775, etc.).

Reviewed by Cursor Bugbot for commit f1331ec. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Unix file permission behavior when marking files as executable, ensuring the corresponding read permission bits are also applied.
    • Updated both synchronous and async permission updates to preserve correct, expected permissions.
  • Tests
    • Added a Unix-only unit test to verify restrictive modes are widened to readable+executable as intended, without changing already-correct permissions.

make_executable only added the execute bits (mode | 0o111), so a
downloaded tempfile created with 0o600 ended up 0o711. For system-wide
installs owned by root this left interpreted executables (e.g. PHP
PHARs like Composer) executable but not readable by non-owner users,
causing "Could not open input file".

Add the matching read bits alongside execute so any class that gains
execute also gains read.

Fixes #11108

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

coderabbitai Bot commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Unix executable handling now adds corresponding read bits when granting execute permissions. Both synchronous and asynchronous paths use the shared mode calculation, with Unix-only tests covering representative permission modes.

Changes

Unix executable permissions

Layer / File(s) Summary
Permission widening and validation
src/file.rs
Synchronous and asynchronous executable helpers now use mode widening that adds read and execute bits, with a Unix-only test covering representative inputs.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: risu729

Poem

I’m a rabbit with permissions bright,
Adding read bits beside execute light.
Sync hops forward, async follows too,
Tests check each mode as they should do.
Unix files now leap with delight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: raw executables now gain read permissions in addition to execute permissions.

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.

@greptile-apps

greptile-apps Bot commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes a real-world failure where downloaded raw executables (e.g. PHP PHARs) were left with mode 0o711 — executable but unreadable by non-owners — causing interpreted runtimes like PHP to error with "Could not open input file". The fix adds a shared executable_mode helper that applies mode | 0o111 | 0o444, so any permission class that gains execute also gains read, without widening write bits.

  • executable_mode is #[cfg(unix)]-gated and called by both make_executable and make_executable_async, centralising the invariant.
  • A focused unit test covers 0o600 → 0o755, 0o640 → 0o755, 0o700 → 0o755, 0o755 → 0o755 (idempotent), and 0o660 → 0o775 (group write preserved).

Confidence Score: 5/5

Safe to merge — tiny, well-tested permission arithmetic that strictly expands readability without touching write bits, fixing a documented runtime failure with no regressions on the existing call sites.

The change is a two-line mode computation (mode | 0o111 | 0o444) with a dedicated unit test, #[cfg(unix)] scoping, and no structural changes to surrounding logic. Both callers are updated consistently, and the idempotent case (0o755 → 0o755) confirms no over-widening occurs for already-correct files.

No files require special attention.

Important Files Changed

Filename Overview
src/file.rs Introduces executable_mode(mode: u32) helper that ORs in both execute (0o111) and read (0o444) bits; updates both sync and async make_executable callers and adds a unit test covering five mode transitions.

Reviews (3): Last reviewed commit: "Revert "refactor(http): use async metada..." | Re-trigger Greptile

@coderabbitai coderabbitai 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.

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 `@src/file.rs`:
- Around line 766-768: Update the permission-setting flow around path.metadata()
to retrieve metadata through Tokio’s asynchronous filesystem API instead of the
synchronous std::fs operation. Preserve the existing permissions mutation via
executable_mode and the subsequent tokio::fs::set_permissions call.
🪄 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: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 586dd1ee-5bd4-4893-909b-a2bf132eae23

📥 Commits

Reviewing files that changed from the base of the PR and between 1a7f9cb and 9c483a2.

📒 Files selected for processing (1)
  • src/file.rs

Comment thread src/file.rs Resolved
jdx and others added 2 commits July 20, 2026 18:53
make_executable_async is async and already uses tokio::fs::set_permissions,
so read metadata via tokio::fs::metadata rather than the blocking std variant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jdx
jdx merged commit 79e37ab into main Jul 20, 2026
34 checks passed
@jdx
jdx deleted the fix/http-raw-file-readable branch July 20, 2026 20:06
@github-actions

Copy link
Copy Markdown
Contributor

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.7.7 x -- echo 23.7 ± 1.7 19.2 30.8 1.02 ± 0.11
mise x -- echo 23.2 ± 1.8 18.7 31.3 1.00

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.7.7 env 23.4 ± 1.7 19.4 29.8 1.03 ± 0.11
mise env 22.8 ± 1.8 18.2 29.6 1.00

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.7.7 hook-env 25.7 ± 1.9 20.5 30.9 1.03 ± 0.13
mise hook-env 24.9 ± 2.5 18.8 35.8 1.00

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.7.7 ls 21.9 ± 2.0 17.4 29.7 1.05 ± 0.15
mise ls 20.7 ± 2.3 15.8 29.6 1.00

xtasks/test/perf

Command mise-2026.7.7 mise Variance
install (cached) 180ms 176ms +2%
ls (cached) 72ms 73ms -1%
bin-paths (cached) 71ms 71ms +0%
task-ls (cached) 139ms 139ms +0%

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