T09 · Insecure Skill Coding Practices
- Location
scripts/setup_ads.py:4- Finding
Google Ads Credential File Is Created Without Restrictive Permissions
- Content
View full analysis
Vulnerability Details
File Location:
scripts/setup_ads.py, lines 4–12
Vulnerability Type: Insecure permissions on a sensitive configuration file
Risk Level: MediumVulnerable Code
python def create_config_template(path): config = { 'developer_token': 'INSERT_DEVELOPER_TOKEN_HERE', 'client_id': 'INSERT_CLIENT_ID_HERE', 'client_secret': 'INSERT_CLIENT_SECRET_HERE', 'refresh_token': 'INSERT_REFRESH_TOKEN_HERE', 'use_proto_plus': True } with open(path, 'w') as f: yaml.dump(config, f, default_flow_style=False)Technical Analysis
The script creates
~/.google-ads.yamlwith Python's standardopen(path, 'w')operation and does not explicitly enforce owner-only permissions. The resulting permissions therefore depend on the process umask. For example, a typical umask of022can produce a file with mode0644, making it readable by other local users.The file initially contains placeholders, but its intended purpose is to store a Google Ads developer token, OAuth client secret, and refresh token. After a user replaces the placeholders with real credentials, permissive file permissions can expose those secrets to other accounts on the same system.
Attack Path
- The user runs
scripts/setup_ads.py. - The script creates
~/.google-ads.yamlwithout explicitly setting mode0600. - The user inserts valid Google Ads credentials into the generated file.
- On a multi-user host with a permissive umask, another local account reads the configuration file.
- The attacker extracts the developer token, client credentials, and refresh token.
- The attacker attempts to reuse those credentials through the Google Ads API.
Exploitation requires local filesystem access under an account permitted to read the resulting file. Actual API access remains constrained by the validity, authorization scope, and account permissions o ...[truncated 622 chars]
- The user runs
- Remediation
View remediation
Remediation Suggestions
Create the credential file atomically with owner-only permissions and refuse to overwrite an existing file:
python import os import yaml def create_config_template(path): config = { 'developer_token': 'INSERT_DEVELOPER_TOKEN_HERE', 'client_id': 'INSERT_CLIENT_ID_HERE', 'client_secret': 'INSERT_CLIENT_SECRET_HERE', 'refresh_token': 'INSERT_REFRESH_TOKEN_HERE', 'use_proto_plus': True, } flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL fd = os.open(path, flags, 0o600) try: with os.fdopen(fd, 'w') as f: yaml.safe_dump(config, f, default_flow_style=False) except Exception: try: os.unlink(path) except OSError: pass raiseAdditional hardening measures:
- Verify the final mode with
os.stat()and reject or correct group/world-readable permissions. - Apply
os.chmod(path, 0o600)where appropriate as a defense-in-depth measure. - Warn users that the file contains sensitive credentials and must not be committed to source control.
- Prefer a platform credential store or secrets manager where operationally feasible.
- Rotate the refresh token, client secret, and developer token if unauthorized file access is suspected.
- Verify the final mode with
