T09 · Insecure Skill Coding Practices
- Location
SKILL.md:83- Finding
OAuth Access and Refresh Tokens Exposed in Console Output
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This Google Docs skill is mostly coherent, but its examples expose live OAuth tokens and under-warn about document-changing API access.
Install only if you are comfortable granting Google Docs API access. Do not run the token examples as written in shared terminals, logs, CI, or agent transcripts; avoid printing refresh or access tokens, store credentials in a proper secret store, and revoke any token that may have been exposed.
SKILL.md:83OAuth Access and Refresh Tokens Exposed in Console Output
SKILL.md:42OAuth Callback Is Not Correlated with State and Does Not Use PKCE
You can close this window.
') server = HTTPServer(('localhost', 8080), OAuthHandler) server.handle_request() # Step 3: Exchange code for tokens if auth_code: data = urllib.parse.urlencode({ 'code': auth_code, 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, 'redirect_uri': REDIRECT_URI, 'grant_type': 'authorization_code' }).encode() req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data) ``` ### Technical Analysis The authorization request does not include a cryptographically random OAuth `state` value, and the callback handler does not validate that the response corresponds to the authorization request initiated by the script. It accepts the first `code` query parameter received by the local HTTP server. The flow also omits Proof Key for Code Exchange (PKCE). PKCE binds the authorization code to a verifier held by the initiating client and ...[truncated 1980 chars]This example prints live OAuth refresh and access tokens directly, exposing secrets that can be reused to access the user's Google Docs data. A leaked refresh token is especially dangerous because it can be exchanged for fresh access tokens long after the initial session ends.
response = json.load(urllib.request.urlopen(req))
print(f"\nRefresh Token: {response['refresh_token']}")
print(f"Access Token: {response['access_token']}")
print(f"\nSet your refresh token:")
print(f"export GOOGLE_REFRESH_TOKEN=\"{response['refresh_token']}\"")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
print(f"export GOOGLE_REFRESH_TOKEN="{response['refresh_token']}"")
### Getting Access Token
Before making API calls, get a fresh access token:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Before making API calls, get a fresh access token:
import urllib.request
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Before making API calls, get a fresh access token:
import urllib.request
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Before making API calls, get a fresh access token:
import urllib.request
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Before making API calls, get a fresh access token:
import urllib.request
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Before making API calls, get a fresh access token:
import urllib.request
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Before making API calls, get a fresh access token:
import urllib.request
This example prints the access token after retrieval, making it easy to leak via logs or console history. Even short-lived access tokens can grant unauthorized document access during their validity period.
access_token = get_access_token() print(f"Access Token: {access_token}")
## Base URL
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
| 429 | Rate Limited - Too many requests |
Access tokens expire after 1 hour. If you get a 401 error, refresh the token:
try:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
## Best Practices
1. **Token Management**
- Cache access tokens (valid for 1 hour)
- Store refresh token securely
- Implement automatic token refresh
The skill describes creating, reading, and modifying Google Docs content but does not clearly warn that document data will be transmitted to Google and that API operations can alter remote documents. This omission can mislead users about data handling and action side effects, increasing the chance of unintended disclosure or destructive changes.
The documentation explicitly prints both the refresh token and access token to stdout, which can expose credentials through terminal history, logs, screen recording, or shared sessions. Because refresh tokens enable ongoing API access, this creates a realistic credential leakage risk even though it appears intended as a convenience example.
Detected: suspicious.exposed_secret_literal