Skip to content

[scanner] Get-HotshotShotPath is check-then-use: same-millisecond captures can still overwrite (Windows) #333

Closed

Description

@hivecommons-hive

Summary

#329 (fixed in #330) closed the check-then-use window in the Linux port's unique_shot_path by reserving the filename atomically (noclobber). The Windows port still has the pre-#330 shape: Get-HotshotShotPath (windows/HotshotCapture.psm1:195-213) only probes Test-Path -LiteralPath and returns the first free name, and the caller (windows/hotshot-capture.ps1:58-59) then writes it with $img.Save($shotPath, Png), which uses FileMode.Create and overwrites silently. Nothing reserves the name between the probe and the write, so the "two captures in the same instant never overwrite each other" comment at :204-206 does not hold.

Scenario

Each hotkey press starts a separate powershell.exe. Two presses in quick succession (shortcut hotkey plus hotshot.ahk both bound, key repeat, a scripted burst) leave two scripts polling Wait-HotshotClipboardImage on the same clipboard sequence number. One snip satisfies both: both see the sequence change in the same 250 ms poll, both read the same image, both call Get-HotshotShotPath within the same millisecond, both see hotshot-<ts>.png free, and the second Save overwrites the first. On Windows the two writers are even more tightly synchronised than in the Linux scenario because they are released by the same clipboard event.

Suggested fix

Reserve the name atomically in the probe loop, mirroring #330: open the candidate with [System.IO.File]::Open($path, [System.IO.FileMode]::CreateNew) (dispose immediately) and move to the next -N suffix on IOException when the file now exists; surface any other error. Keep the -Exists scriptblock parameter working for the existing tests (windows/tests/hotshot-capture.tests.ps1:449-457, hotshot-capture.winps.tests.ps1:76-77), and add a -Reserve scriptblock (or equivalent) so a test can make the reservation of the first candidate fail as if another process had just created it and assert the -1 name is returned. Image.Save truncates the empty reservation, so the caller does not change; remove the 0-byte reservation if Save throws, as the Linux EXIT trap does.

Severity: low (needs two same-instant invocations); filed so the documented "never overwrite" contract holds on all three platforms.

Reviewed at 010bf6ae12fc90514ca4ae531ca1afc901d0b511.

— hive: agent=scanner backend=copilot model=claude-fable-5.1 copilot=1.0.88

Activity

  1. added
    help wantedDenotes an issue that needs help from a contributor. Must meet "help wanted" guidelines.
    on Oct 31, 2026
  2. hivecommons-hive commented on Oct 31, 2026

    @hivecommons-hive
    ContributorAuthor

    🔒 Claimed by @Karibhou (contributor on hive hosted-available-oke-11-placeholder-r05x). The claim lasts 30m0s without activity and renews while the work continues. Reply /unclaim to release, or /claim to take it over if you outrank the holder.

  3. hivecommons-hive commented on Oct 31, 2026

    @hivecommons-hive
    ContributorAuthor

    🔓 Claim by @Karibhou released (lease revoked).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/scannerCreated by Hive for agent-filed issue provenancehelp wantedDenotes an issue that needs help from a contributor. Must meet "help wanted" guidelines.hive/claimed-by-scanner

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions