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
fix(provision): WiFi password persisted world-readable (0644) in per-port state file #1754
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) calls os.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, files 0644.
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.
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) and os.chmod the directory on the
existing-directory path, since makedirs does not alter an existing one.
Create the temp file restrictively (e.g. os.open(..., 0o600) or os.chmod before 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.
Also ensure the state directory is created with restricted permissions:
os.makedirs(state_dir, mode=0o700, exist_ok=True)
As a follow-up: the WiFi password only needs to be stored if re-provisioning without re-entering credentials is a use case. If callers always pass credentials explicitly, consider dropping the password from the persisted state entirely to eliminate the at-rest exposure.
There's a second place the Wi-Fi password leaks: provision.py --state prints the stored record as JSON with the password in clear. Checked on current main with a dry-run and placeholder credentials. MEASURED. The state file's 0644 mode is CODE-DERIVED (provision.pysave_state, no mode=/chmod; unchanged at 03a89793).
Users paste --state output into issues when they ask for help, so this ends up in public threads.
Masking secrets in --state, or not storing them at all as #1832 proposes, would stop them ending up in pasted support logs. Whichever PR lands should cover this path, and 0600 on the state file would be a sensible default.
Activity
ruvnet commentedon Sep 30, 2026
This is a real security issue on multi-user systems. The minimal fix in
provision.py:Also ensure the state directory is created with restricted permissions:
As a follow-up: the WiFi password only needs to be stored if re-provisioning without re-entering credentials is a use case. If callers always pass credentials explicitly, consider dropping the password from the persisted state entirely to eliminate the at-rest exposure.
Generated by Claude Code
aepod commentedon Oct 2, 2026
There's a second place the Wi-Fi password leaks:
provision.py --stateprints the stored record as JSON with the password in clear. Checked on current main with a dry-run and placeholder credentials. MEASURED. The state file's 0644 mode is CODE-DERIVED (provision.pysave_state, nomode=/chmod; unchanged at03a89793).Users paste
--stateoutput into issues when they ask for help, so this ends up in public threads.Masking secrets in
--state, or not storing them at all as #1832 proposes, would stop them ending up in pasted support logs. Whichever PR lands should cover this path, and 0600 on the state file would be a sensible default.fix(provision): keep the WiFi password owner-only and out of --state …
feat(provision): read the WiFi password from a file or a prompt