Skip to content

fix(bin): skip the PR-poll mode assertion only where the filesystem cannot express modes - #2919

Open
3min-lang wants to merge 3 commits into
kunchenguid:mainfrom
3min-lang:windows-mode-incapable-fs
Open

fix(bin): skip the PR-poll mode assertion only where the filesystem cannot express modes#2919
3min-lang wants to merge 3 commits into
kunchenguid:mainfrom
3min-lang:windows-mode-incapable-fs

Conversation

@3min-lang

Copy link
Copy Markdown

Problem

Git for Windows mounts NTFS with noacl, where chmod is a silent no-op and
the mode is decided solely by the umask at creation time. fm_pr_private_file_valid
asserts mode 600, so on that platform the assertion can never hold and
fm-pr-check.sh always exits with error: could not prepare PR poll.

The consequence is not cosmetic: no merge poll is ever armed on Windows.
Finished ship tasks go stale instead of being tracked to landing, the merged-PR
wake never fires, and teardown never runs.

Fix

Probe the actual directory rather than inferring capability from uname or
mount flags, since one host can hold both mode-capable and mode-incapable
filesystems.

The probe checks both directions (600 then 644). A single chmod check is
not sufficient: fm_pr_poll_prepare runs under umask 077, so a fresh
mktemp file already reads 600 and a one-way check cannot distinguish a
working chmod from a no-op. This was found by running the real path, not in
review.

A probe that cannot complete reports capable, so an unexplained failure keeps
the strict assertion rather than silently relaxing it. The other three
guarantees in fm_pr_private_file_valid (regular file and not a symlink, same
device, single hard link) are untouched, and they carry the substantive
protection where modes cannot be expressed.

This is not the only mode assertion affected

fm_backend_herdr_presentation_lock_namespace_valid requires mode 700 on
/tmp/firstmate-herdr-presentation. Git Bash mounts /tmp with noacl too, so
mkdir -m 700 is a no-op there and the directory reads 755, which is why
herdr presentation focus lock unavailable is reported on every spawn on this
host. That one is out of scope here, but it suggests the pattern is worth a
shared helper if you would prefer that shape.

Testing

  • bin/fm-lint.sh bin/fm-pr-lib.sh clean.
  • On Windows 11 / Git for Windows: fm-pr-check.sh now arms the poll, the poll
    reported merged for a real PR, and the artifacts retired.
  • Regression check with the probe cache forced to "capable": a file whose mode
    does not match is still rejected, and a matching one is still accepted, so
    behaviour on mode-capable filesystems is unchanged.
  • tests/fm-pr-check-security.test.sh fails on this host with
    not ok - parser accepted a rejected raw-byte URL class. That failure
    reproduces identically on unpatched main here, so it is pre-existing and
    unrelated to this change, not introduced by it.

I did not add a test, because simulating a mode-incapable filesystem portably
is not obvious and a misleading test seemed worse than none. Happy to add one
in whatever shape you prefer.

🤖 Generated with Claude Code

…annot express modes

Git Bash mounts NTFS with `noacl`, so chmod is a silent no-op there and the
mode is decided solely by the umask at creation time. `fm_pr_private_file_valid`
asserts mode 600, which can therefore never hold, so `fm-pr-check.sh` always
failed with `error: could not prepare PR poll`. No merge poll was ever armed on
Windows: finished ship tasks went stale instead of being tracked to landing, and
teardown never ran.

Probe the actual directory instead of inferring capability from `uname` or mount
flags, because one host can hold both mode-capable and mode-incapable
filesystems. The probe checks both directions (600 then 644): callers run under
`umask 077`, where a fresh mktemp file already reads 600, so a single chmod
check cannot tell a working chmod from a no-op.

A probe that cannot complete reports capable, so an unexplained failure keeps
the strict assertion rather than silently relaxing it. The remaining three
guarantees - regular file, same device, single hard link - are untouched, and
they carry the substantive protection on filesystems that cannot express modes.
@greptile-apps

greptile-apps Bot commented Aug 24, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Reviews (2): Last reviewed commit: "style(bin): declare the probe locals wit..." | Re-trigger Greptile

Comment thread bin/fm-pr-lib.sh Outdated
Review caught that only the mktemp failure path kept the strict assertion.
Once the probe file existed, a failed chmod or an unreadable mode produced an
observation that was not exactly 600/644, which the previous code classified
as mode-incapable and cached, so later private files in that directory skipped
mode validation. The comment claimed a fail-closed property the code did not
have.

Classify only complete, unambiguous observations. Capable means both requested
modes were read back. Incapable means the same mode was read back twice, which
is chmod demonstrably doing nothing. Every other outcome reports capable, keeps
the strict assertion, and is deliberately not cached so a transient failure
cannot be remembered as evidence.
@3min-lang

Copy link
Copy Markdown
Author

Thanks - the security finding is correct and now fixed in 60701e0 (plus a lint follow-up in d4ba107).

You were right that the fail-closed guarantee only covered the mktemp failure path. Once the probe file existed, a failed chmod or an unreadable mode produced an observation that was not exactly 600/644, which the old code classified as mode-incapable and cached. The comment above it claimed a property the code did not have, which is the part I am least happy about.

The probe now classifies only complete, unambiguous observations:

  • capable: both requested modes were read back (600 then 644)
  • incapable: the same mode was read back twice, which is chmod demonstrably doing nothing
  • anything else: reports capable, keeps the strict assertion, and is deliberately not cached, so a transient failure cannot be remembered as evidence

Verified on the affected host: the noacl mount still classifies as incapable both under the default umask and under umask 077, and a forced mode-read failure now reports capable so the strict assertion stands.

On regression coverage: I would rather add the test in your preferred shape than guess. Simulating a mode-incapable filesystem portably is the hard part. If a test that stubs fm_pr_file_mode to drive the three classifications is acceptable in this suite, I will add it here.

🤖 Generated with Claude Code

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.

1 participant