T09 · Insecure Skill Coding Practices
- Location
utils.py:82- Finding
API Key Stored Without Enforced Restrictive File Permissions
- Content
View full analysis
Vulnerability Details
File Location:
utils.py:6-8andutils.py:82-84
Vulnerability Type: Plaintext credential storage with unsafe default permissions
Risk Level: MediumVulnerable Code
python JIUMA_API_KEY_SAVE_DIR = f"{os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))}/.jiuma" os.makedirs(JIUMA_API_KEY_SAVE_DIR, exist_ok=True) JIUMA_API_KEY_SAVE_PATH = f"{JIUMA_API_KEY_SAVE_DIR}/jiuma_api_key"python def save_jiuma_api_key(api_key): with open(JIUMA_API_KEY_SAVE_PATH, "w") as f: f.write(api_key)Technical Analysis
The Skill stores the Jiuma API key as plaintext in a predictable file but does not explicitly restrict the permissions of either the containing directory or the credential file. The effective permissions therefore depend on the process umask.
With common default settings, the directory may be created with mode
0755and the file with mode0644. On a multi-user system, this can make the API key readable by other local accounts. This also conflicts with the documentation's assertion that only the current user can read the key.The code additionally opens a predictable path without explicitly rejecting symbolic links or using an atomic, exclusive file creation operation. If an attacker can modify the containing directory, this may permit redirection of the credential write.
Attack Path
- A user completes the Jiuma QR-code login process.
login.pyreceives the user'ssecret_keyand passes it tosave_jiuma_api_key.utils.pywrites the key to.jiuma/jiuma_api_keyusing permissions derived from the current umask.- Another local user or compromised process locates the predictable credential file.
- If the resulting permissions allow access, the attacker reads the plaintext API key.
- The attacker submits authenticated requests to Jiuma using the stolen
X-Secret-Keyvalue.
Exploitation require ...[truncated 623 chars]
- Remediation
View remediation
Remediation Suggestions
- Create the credential directory with mode
0700. - Create the credential file atomically with mode
0600, rather than relying on the process umask. - Apply restrictive permissions to existing directories and files during migration.
- Reject symbolic links and other unexpected file types before reading or writing the credential.
- Use atomic replacement to avoid partially written credential files.
- Prefer an operating-system credential store or keyring instead of a plaintext file where available.
- Avoid suppressing all credential-read exceptions; distinguish missing files from permission and integrity failures.
- Ensure the documented storage path and permission guarantees match the implementation.
Example hardening pattern:
python os.makedirs(JIUMA_API_KEY_SAVE_DIR, mode=0o700, exist_ok=True) os.chmod(JIUMA_API_KEY_SAVE_DIR, 0o700) flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC if hasattr(os, "O_NOFOLLOW"): flags |= os.O_NOFOLLOW fd = os.open(JIUMA_API_KEY_SAVE_PATH, flags, 0o600) try: os.fchmod(fd, 0o600) with os.fdopen(fd, "w") as f: f.write(api_key) except Exception: os.close(fd) raise- Create the credential directory with mode
