Repository navigation
Make a secret file usable on Windows, and stop two tests passing for the wrong reason - #107
Merged
Merged
Conversation
…the wrong reason `secrets.py` refused any file with the group or other read bits set, and told the user to run `chmod 600`. On Windows CPython synthesises st_mode as 0o666 for every regular file and os.chmod cannot clear those bits, so the guard was true of every file on the system: reading a secret from a file was impossible on a platform this ships a binary for, and the advice offered could not change the answer. Measured, not inferred - chmod 600 leaves the mode at 0o100666. The check is skipped where the bits carry no information rather than approximated. Real permissions on Windows are ACLs, which stat cannot see, and reading them needs icacls or pywin32 - a large dependency for a guard that is advisory even on POSIX. Leaving it was not the safer option: it made the safest input route unusable and pushed people to the environment variable instead. POSIX behaviour is untouched. Three permission tests are now skipped there, and **two of them were passing for the wrong reason** - they assert that an insecure file is refused, which was true because every file was. A skip that says so is worth more than a green tick that means nothing. The third also failed for an unrelated reason: pytest.raises(match=) is a regex and a Windows path is full of backslashes, so it now uses re.escape. test_device.py had the same shape - it asserts the stored device identity is readable by its owner alone, which chmod cannot arrange there. Skipped, and device.py now says plainly that its chmod is a no-op on Windows rather than leaving it to look like protection. The marker lives in test/unittestutils.py beside skip_unless_unix, which is where this repository already keeps that kind of thing. Windows goes from 8 failures to none: 435 passed, 8 skipped. POSIX is unchanged by construction - `os.name != "nt" and ...` - and CI proves it, because the four skipped tests are the ones that run there. Also brings docs/android-import-handover.md back in line with the code. §6's three outstanding items have shipped, the device-name ask is dropped rather than pending - announce_device needs the heartbeat token, which an account serialised before FindMy.py kept it does not have - and §4 no longer asks for a Room migration, because OwnedBeacons.content is already nullable. The risk there moved to the read paths, and the note now says which one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
saamdotexe
pushed a commit
to saamdotexe/OpenTagViewer
that referenced
this pull request
Oct 22, 2026
…s-on-windows Make a secret file usable on Windows, and stop two tests passing for the wrong reason
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Branched from
main, so it is independent of #106 and can go in either order.Reading a secret from a file did not work on Windows at all
secrets.pyrefuses a file with the group or other read bits set and tells the user to runchmod 600. Measured, not inferred:CPython synthesises
st_modeas0o666for every regular file on Windows, andos.chmodcannotclear those bits. So the guard was true of every file on the system — the safest way to pass a
secret was unusable on a platform this ships a binary for, and the advice it printed could not
change the answer.
The check is now skipped where the bits carry no information, rather than approximated. Real
permissions there are ACLs, which
statcannot see; reading them needsicaclsor pywin32, whichis a large dependency for a guard that is advisory even on POSIX. Leaving it was not the safer
option — it made the best input route impossible and pushed people to the environment variable,
which the module's own docstring ranks as worse. POSIX behaviour is untouched.
Two tests were passing for the wrong reason
This is the part worth reviewing. Of the three permission tests, two assert that an insecure file
is refused — and on Windows they passed, because every file was refused. Green ticks over an
assertion that could not fail.
They are skipped there now, with a reason that says so. The third failed for a second, unrelated
cause:
pytest.raises(match=...)is a regex and a Windows path is full of backslashes, so it needsre.escape.test_device.pyhad the same shape — it asserts the stored device identity is readable by itsowner alone, which
chmodcannot arrange there. Skipped, anddevice.pynow says plainly that itschmodis a no-op on Windows instead of leaving it to look like protection.The marker lives in
test/unittestutils.pybesideskip_unless_unix, which is where this repoalready keeps these.
Result
os.name != "nt" and …POSIX is unchanged by construction, and CI proves it: the four skipped tests are exactly the ones
that run there, so if the guard had been weakened they would go red.
Also: the handover doc back in line with the code
docs/android-import-handover.mdhad drifted into being confidently wrong.§6's three outstanding items have shipped — the pin, the serial, the iPhone model.
The device-name ask is dropped, not pending.
announce_deviceneeds thecom.apple.gs.idms.hbheartbeat token, which an account serialised before FindMy.py startedkeeping it does not have. Making it work means those users signing in again, which is not worth
a re-login for a label.
§4 no longer asks for a Room migration.
OwnedBeacons.contentis already nullable in schema3, so a key-list tag is a row with
accessory_jsonset andcontentNULL. No new column, nomigration, and rule 1 does not come into it.
The risk moved rather than vanished, and the note now names where:
BeaconRepositoryalreadytolerates a NULL plist, but
BeaconDataParserreadsgetOwnedBeaconInfo().contentstraight intoan XML parse, and a custom tag has no naming record either — its display name is the
nameinits own mapping.