T09 · Insecure Skill Coding Practices
- Location
references/oauth-flows.md:6- Finding
OAuth callback accepts authorization codes without validating the state parameter
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill is a credential setup guide for social platforms, but it gives high-impact token storage and OAuth flow guidance without enough safety controls.
Review before installing. This skill may be useful as a platform API checklist, but do not follow its credential storage pattern as written. Store OAuth tokens and app secrets in a secret manager or encrypted store, use least-privilege scopes, validate OAuth state and PKCE where applicable, redact tokens from logs and URLs, and define token rotation and revocation procedures.
references/oauth-flows.md:6OAuth callback accepts authorization codes without validating the state parameter
SKILL.md:128Reusable platform credentials are stored directly in a generic JSONB column without prescribed protection
SKILL.md:29Facebook application secret and OAuth tokens are placed in URL query strings
The skill explicitly instructs storing API credentials, OAuth access tokens, refresh tokens, and app secrets in a database, but provides no safeguards such as encryption, secret scoping, access control, rotation, or redaction guidance. In this context, the skill handles highly sensitive platform credentials, so normalizing plaintext or loosely governed storage materially increases the risk of account takeover and long-term unauthorized API access.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
3. In Graph API Explorer (`developers.facebook.com/tools/explorer/`):
- Select your app
- Add permissions: `pages_show_list`, `pages_read_engagement`, `pages_manage_posts`, `instagram_basic`, `instagram_content_publish`
- Generate User Access Token → authorize
- Exchange for long-lived token: `GET /oauth/access_token?grant_type=fb_exchange_token&client_id={app_id}&client_secret={secret}&fb_exchange_token={short_token}`
4. Get Page Access Token: `GET /me/accounts` → find page → copy `access_token`
5. Get Instagram Business Account ID: `GET /{page_id}?fields=instagram_business_account`
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
3. In Graph API Explorer (`developers.facebook.com/tools/explorer/`):
- Select your app
- Add permissions: `pages_show_list`, `pages_read_engagement`, `pages_manage_posts`, `instagram_basic`, `instagram_content_publish`
- Generate User Access Token → authorize
- Exchange for long-lived token: `GET /oauth/access_token?grant_type=fb_exchange_token&client_id={app_id}&client_secret={secret}&fb_exchange_token={short_token}`
4. Get Page Access Token: `GET /me/accounts` → find page → copy `access_token`
5. Get Instagram Business Account ID: `GET /{page_id}?fields=instagram_business_account`
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
3. In Graph API Explorer (`developers.facebook.com/tools/explorer/`):
- Select your app
- Add permissions: `pages_show_list`, `pages_read_engagement`, `pages_manage_posts`, `instagram_basic`, `instagram_content_publish`
- Generate User Access Token → authorize
- Exchange for long-lived token: `GET /oauth/access_token?grant_type=fb_exchange_token&client_id={app_id}&client_secret={secret}&fb_exchange_token={short_token}`
4. Get Page Access Token: `GET /me/accounts` → find page → copy `access_token`
5. Get Instagram Business Account ID: `GET /{page_id}?fields=instagram_business_account`
The skill explicitly instructs users to copy a page access token and then presents a storage pattern for persisting credentials, without any security controls. In this context, page access tokens can grant durable posting and account-management access, so encouraging manual copying and persistence without secret-handling safeguards creates a realistic credential-compromise path.
- Add permissions: `pages_show_list`, `pages_read_engagement`, `pages_manage_posts`, `instagram_basic`, `instagram_content_publish`
- Generate User Access Token → authorize
- Exchange for long-lived token: `GET /oauth/access_token?grant_type=fb_exchange_token&client_id={app_id}&client_secret={secret}&fb_exchange_token={short_token}`
4. Get Page Access Token: `GET /me/accounts` → find page → copy `access_token`
5. Get Instagram Business Account ID: `GET /{page_id}?fields=instagram_business_account`
### Store
The statement that page tokens derived from long-lived user tokens are permanent encourages retention of effectively non-expiring credentials without corresponding security warnings or compensating controls. In a credential-management skill, emphasizing permanence increases danger because a leaked token may provide indefinite unauthorized access.
### Key gotcha
Page Access Tokens from Graph API Explorer are **short-lived** unless you exchange the User Token for a long-lived one FIRST, then request Page tokens from the long-lived User Token. Page tokens derived from long-lived user tokens are **permanent** (no expiry).
## YouTube
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from google_auth_oauthlib.flow import InstalledAppFlow
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json',
scopes=['https://www.googleapis.com/auth/youtube.upload',
'https://www.googleapis.com/auth/youtube.readonly']
)
The description says to use the skill for 'setting up API access for any social platform, refreshing expired OAuth tokens, or debugging authentication flows,' which is a very broad trigger scope without explicit constraints or exclusion conditions. This ambiguity increases the chance of unintended invocation for generic auth or API-debugging tasks outside the intended platform-connector context.