You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
net5.0/net6.0/netstandard builds pick a non-executable file earlier on the overlay PATH, so the command fails with "Permission denied" instead of running the executable copy later on #139
TryExecutable (RunCommand/RunCommand.cs, around line 510) skips files with no execute bit. That check sits inside #if NET7_0_OR_GREATER, because it uses File.GetUnixFileMode. The net5.0, net6.0, netstandard2.0 and netstandard2.1 builds therefore accept any file that exists.
On Linux and macOS those builds can pick a non-executable file over a real executable later on the overlay PATH. Neither the README ("On other platforms the file must have an execute bit") nor the method's XML doc mentions this limit.
ExecuteAsyncShouldSkipANonExecutableFileOnTheOverlayPath passes only because the test project targets net10.0, so it never exercises those builds. The behaviour came in with #137.
Repro (Linux)
The repro is a net6.0 console app that references the library's net6.0 build. The #if is decided at compile time, so the runtime version doesn't matter.
/tmp/rc-noexec/rctool mode 644, prints "non-executable copy"
/tmp/rc-exec/rctool mode 755, prints "executable copy"
Observed:Win32Exception: An error occurred trying to start process '/tmp/rc-noexec/rctool' ... Permission denied
Expected:executable copy, which is what PATH=/tmp/rc-noexec:/tmp/rc-exec sh -c rctool prints.
Suggested fix
Builds below net7: on non-Windows platforms, check with libc access(path, X_OK) through P/Invoke. Also consider doing this on net7+. The current "any execute bit" test accepts a 0700 file owned by another user, which the caller can't run, so a shell would skip it and RunCommand doesn't.
Tests: run the overlay-PATH tests against a pre-net7 target too, or add a unit test of TryExecutable that is compiled for those TFMs.
Priority: Medium. On Linux and macOS, the net5.0, net6.0 and netstandard builds accept a non-executable file found earlier on the overlay PATH, so a command that a shell would run fails with "Permission denied". The affected TFMs are older, but netstandard2.0 is what Mono, Unity and other non-net7 hosts consume. The existing test only targets net10.0, so it never sees the gap.
Area / suggested assignment:TryExecutable under the #if NET7_0_OR_GREATER guard in RunCommand/RunCommand.cs, and the test project's target frameworks.
Next step: Add a libc access(path, X_OK) P/Invoke for non-Windows platforms below net7. Consider using it on net7+ as well, because it correctly rejects a 0700 file the caller doesn't own. Add a pre-net7 test target, or a TryExecutable unit test compiled for those TFMs, so the check is actually exercised. Mention the limit in the README until the fix lands.
Activity
matt-edmondson commentedon Nov 4, 2026
Triage
PATH, so a command that a shell would run fails with "Permission denied". The affected TFMs are older, but netstandard2.0 is what Mono, Unity and other non-net7 hosts consume. The existing test only targets net10.0, so it never sees the gap.TryExecutableunder the#if NET7_0_OR_GREATERguard inRunCommand/RunCommand.cs, and the test project's target frameworks.npmnow resolves tonpm.cmd, so cmd.exe re-parses the arguments and&,|,%in them run as commands #138, so the two fixes could ship together.access(path, X_OK)P/Invoke for non-Windows platforms below net7. Consider using it on net7+ as well, because it correctly rejects a0700file the caller doesn't own. Add a pre-net7 test target, or aTryExecutableunit test compiled for those TFMs, so the check is actually exercised. Mention the limit in the README until the fix lands.Generated by Claude Code