T09 · Insecure Skill Coding Practices
- Location
scripts/gmncode_usage.py:56- Finding
Bearer Token Cache Is Not Created with Atomic Restrictive Permissions
- Content
View full analysis
Vulnerability Details
File Location:
scripts/gmncode_usage.py:56-72
Related Token Storage Logic:scripts/gmncode_usage.py:127-132
Vulnerability Type: Insecure sensitive-file creation and silent permission failure
Risk Level: MediumVulnerable Code
python def ensure_file_mode(path: pathlib.Path, mode: int) -> None: try: path.chmod(mode) except OSError: pass def secure_write_json(path: pathlib.Path, payload: dict[str, Any]) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8") ensure_file_mode(path, stat.S_IRUSR | stat.S_IWUSR)The affected function stores the access token through the following call:
python def _save_cached_token(self, access_token: str, expires_in: int) -> None: expires_at = int(dt.datetime.now(dt.timezone.utc).timestamp()) + int(expires_in) secure_write_json(TOKEN_CACHE, { "access_token": access_token, "expires_at": expires_at, "base_url": self.base_url, })Technical Analysis
The cache file is created or truncated by
Path.write_text()before mode0600is applied. Its initial permissions therefore depend on the process umask. With a common umask of022, a newly created file may initially have mode0644, creating a time-of-check/time-of-use exposure window in which another local user can read the bearer token.The implementation also suppresses every
OSErrorraised bychmod(). If permission hardening fails because of filesystem behavior, ownership, access-control rules, or another operating-system error, execution continues while the sensitive file may remain readable by unintended users.Additionally,
Path.write_text()follows symbolic links. The fixed cache location reduces practical exploitability, but an attacker who can manipulate the cache path or its parent directories could potentially redirect the token write. The code do ...[truncated 1626 chars]- Remediation
View remediation
Remediation Suggestions
- Create the cache directory with mode
0700and verify that it is owned by the current user. - Create the token file with mode
0600at creation time rather than applying permissions after writing. - Use
os.open()with restrictive and defensive flags where supported:
python flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC if hasattr(os, "O_NOFOLLOW"): flags |= os.O_NOFOLLOW fd = os.open(path, flags, 0o600) try: with os.fdopen(fd, "w", encoding="utf-8") as handle: json.dump(payload, handle, ensure_ascii=False) handle.flush() os.fsync(handle.fileno()) except Exception: try: path.unlink() except OSError: pass raise- Prefer writing to a securely created mode-
0600temporary file in the same directory and then replacing the destination atomically withos.replace(). - Reject symbolic links and validate that both the cache directory and destination are regular files owned by the current user.
- Do not suppress permission-setting failures. Abort token caching, remove any insecurely created file, and report a clear error.
- Consider continuing without persistent caching when secure storage cannot be guaranteed.
- Add tests using permissive umasks and simulated
chmod()failures to verify that the token is never observable with group or world permissions.
- Create the cache directory with mode
