T09 · Insecure Skill Coding Practices
- Location
internal/config/config.go:45- Finding
OAuth Credentials and Tokens Stored with Unsafe File Permissions
- Content
View full analysis
Vulnerability Details
File Location:
internal/config/config.go:45-66
Vulnerability Type: Insecure storage of sensitive OAuth credentials
Risk Level: HighVulnerable Code
go func SaveConfig(config *Config) error { dir, err := GetConfigDir() if err != nil { return err } if err := os.MkdirAll(dir, 0755); err != nil { return err } path := filepath.Join(dir, "config.json") file, err := os.Create(path) if err != nil { return err } defer file.Close() return json.NewEncoder(file).Encode(config) }The object serialized by this function contains all of the following sensitive values:
go type Config struct { ClientID string `json:"client_id"` ClientSecret string `json:"client_secret"` AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` Expiry time.Time `json:"expiry"` }Technical Analysis
os.Createcreates a new file with permission mode0666, modified by the process umask. With a common umask of0022, the resultingconfig.jsonis mode0644and therefore readable by other local users. The containing configuration directory is also created with mode0755.The file stores the OAuth client secret, bearer access token, and refresh token in plaintext JSON. A refresh token is particularly sensitive because it may permit continued generation of access tokens after the original access token expires.
The network use of these credentials is otherwise consistent with the declared functionality: the tokens are sent to official Oura HTTPS endpoints. The vulnerability is the insufficient protection of the locally persisted credential material.
Attack Path
- The victim runs the OAuth authentication command.
- The application receives an access token and refresh token from Oura.
SaveConfigseri ...[truncated 1076 chars]
- Remediation
View remediation
Remediation Suggestions
-
Create the configuration directory with mode
0700rather than0755. -
Create or truncate the file explicitly with mode
0600:go if err := os.MkdirAll(dir, 0700); err != nil { return err } if err := os.Chmod(dir, 0700); err != nil { return err } file, err := os.OpenFile( path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600, ) if err != nil { return err } if err := file.Chmod(0600); err != nil { file.Close() return err } -
Repair permissions on existing directories and files, since changing only creation modes does not necessarily fix previously created objects.
-
Prefer an operating-system credential manager or keychain for the client secret and refresh token.
-
Use atomic secure writes to prevent partial configuration files while preserving restrictive permissions.
-
Document the sensitivity and exact storage location of the credential file.
-
Recommend revoking and reissuing tokens if an existing configuration file was exposed.
-
