fix(provision): WiFi password persisted world-readable (0644) in per-port state file
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 94.5k
- Forks
- 12.5k
- Avg merge
- 21h 27m
- Merged PRs (30d)
- 43
Description
Summary
provision.py persists the WiFi password in cleartext to a per-port state file created with
default permissions, so it lands world-readable (0644) under the default umask on macOS and
mainstream Linux.
- The password is deliberately kept in the merged state dict (
provision.py:103) and
round-tripping it is covered by tests (tests/test_provision_state.py:37), so persistence
is intentional — the issue is only how it is protected on disk. save_state()(provision.py:147-157) callsos.makedirs(state_dir, exist_ok=True)and
open(tmp, "w")with no mode argument. Both take whatever the umask allows.- Observed on a real host: state directory
0755, files0644.
Why now
Found while provisioning several nodes for hardware testing. Any local process or user on a
shared machine can read the WiFi credentials of every network the operator has provisioned.
Reproduction
$ python3 provision.py --port <PORT> --ssid <SSID> --password <PW> --target-ip <IP>
$ ls -l ~/.config/wifi-densepose/esp32-provision-state/
-rw-r--r-- ... _dev_cu.usbmodemXXXX.json
$ python3 -c "import json;print('password' in json.load(open('<that file>')))"
True
Done when
The state directory is 0700 and state files are 0600, on both a fresh install and an
existing directory created by an earlier version.
Work items
-
os.makedirs(state_dir, mode=0o700, exist_ok=True)andos.chmodthe directory on the
existing-directory path, sincemakedirsdoes not alter an existing one. - Create the temp file restrictively (e.g.
os.open(..., 0o600)oros.chmodbefore
os.replace) so the secret is never briefly world-readable. - Tighten permissions on directories/files left behind by earlier versions.
- Consider whether the password needs to be persisted at all, or could be prompted per
invocation — that would remove the exposure rather than narrow it.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in provision.py, especially save_state() at lines 147-157, and review the state round-trip coverage in tests/test_provision_state.py. Verify directory and state-file permissions for both fresh and pre-existing paths, including the temporary file before replacement. Done means directories are 0700 and files are 0600 without briefly exposing the password.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100