T09 · Insecure Skill Coding Practices
- Location
scripts/dropbox.py:20- Finding
Dropbox OAuth credentials are stored without enforcing restrictive file permissions
- Content
View full analysis
Vulnerability Details
File Location:
scripts/dropbox.py:20-27andscripts/dropbox.py:36-44
Vulnerability Type: Insecure storage of plaintext OAuth credentials
Risk Level: MediumThe Skill stores the Dropbox application secret, long-lived refresh token, and access token in
~/.config/atlas/dropbox.env. It neither verifies the file's ownership and permissions when reading it nor enforces restrictive permissions when creating or updating it.python CONFIG_PATH = Path.home() / ".config/atlas/dropbox.env" # Global config cache _config = None def load_config(): """Load all credentials from the .env file.""" global _config if _config is not None: return _config config = {} with open(CONFIG_PATH) as f: for line in f: line = line.strip() if line and not line.startswith("#") and "=" in line: key, value = line.split("=", 1) config[key.strip()] = value.strip() _config = config return configpython def save_config(config): """Save config back to .env file.""" global _config lines = [] for key, value in config.items(): lines.append(f"{key}={value}") with open(CONFIG_PATH, 'w') as f: f.write("\n".join(lines) + "\n") _config = configTechnical Analysis
Opening the credential file with
open(CONFIG_PATH, 'w')does not explicitly assign mode0600. When a new file is created, its permissions depend on the process umask. When an existing file is rewritten, any previously permissive mode generally remains in place.The corresponding setup instructions place all of the following sensitive values in this file:
DROPBOX_APP_SECRETDROPBOX_REFRESH_TOKENDROPBOX_ACCESS_TOKEN
The refresh token is particularly sensitive because it is long-lived and can be exchanged for new access to ...[truncated 1729 chars]
- Remediation
View remediation
Remediation Suggestions
- Create
~/.config/atlaswith mode0700. - Create the credential file using an API that explicitly requests mode
0600, such asos.openwithO_CREAT,O_WRONLY, andO_TRUNC. - Call
os.chmod(CONFIG_PATH, 0o600)after creation and before storing credentials. - Before reading, use
lstatto verify that the path is a regular file, is owned by the current user, and has no group or other permission bits. - Reject symbolic links to reduce the risk of writing credentials through an attacker-controlled path.
- Update the file atomically through a securely created temporary file in the same protected directory, then replace the original.
- Where supported, store the refresh token and app secret in an operating-system credential manager instead of a plaintext environment file.
- Document the minimum manual hardening commands:
bash chmod 700 ~/.config/atlas chmod 600 ~/.config/atlas/dropbox.env- Recommend Dropbox “App folder” access by default and reserve “Full Dropbox” access for workflows that explicitly require it.
- Create
