55#
66
77import os
8+ import stat
9+ import warnings
810from typing import Optional
911
1012import toml
@@ -47,6 +49,18 @@ def _read_user_config():
4749
4850 if not all (isinstance (e , dict ) for e in org_sections .values ()):
4951 config_problem = "Pipecat Cloud config file is not valid TOML. Organization sections must be dictionaries. Please log out and log back in."
52+
53+ # Warn if credentials file is readable by group or others (Unix only).
54+ if os .name != "nt" :
55+ file_mode = os .stat (user_config_path ).st_mode
56+ if file_mode & (stat .S_IRWXG | stat .S_IRWXO ):
57+ warnings .warn (
58+ f"Credentials file '{ user_config_path } ' has overly permissive "
59+ f"permissions ({ stat .filemode (file_mode )} ). Other users on this "
60+ f"system may be able to read your token. Run "
61+ f"'chmod 600 { user_config_path } ' to fix, or login again to repair automatically." ,
62+ stacklevel = 2 ,
63+ )
5064 if config_problem :
5165 raise ConfigError (config_problem )
5266
@@ -63,6 +77,12 @@ def _write_user_config(new_config):
6377 with open (user_config_path , "w" ) as f :
6478 toml .dump (new_config , f )
6579
80+ # Restrict permissions so only the file owner can access credentials.
81+ # On Windows os.chmod is limited but home directories are already
82+ # protected by ACLs, so this is effectively a Unix-only hardening.
83+ os .chmod (dir_path , stat .S_IRWXU ) # 0o700
84+ os .chmod (user_config_path , stat .S_IRUSR | stat .S_IWUSR ) # 0o600
85+
6686
6787def remove_user_config ():
6888 os .remove (user_config_path )
0 commit comments