T09 · Insecure Skill Coding Practices
Warning
- Location
- index.js:51
- Finding
- Import-Time Authentication Uses Every Configured Feishu Account<![CDATA[ ## Vulnerability Details **File Location**: `index.js:51`, `index.js:88-110`, `index.js:131-151`, `index.js:326` **Vulnerability Type**: Excessive credential use and unexpected import-time network activity **Risk Level**: Medium ### Complete Code Snippet ```javascript // Initialize bots info (async, but we trigger it) this._ensureBotInfos().catch(err => log('ERROR', 'Failed to ensure bot infos:', err.message)); ``` ```javascript // Fetch from API // We only need to fetch for accounts that have credentials const promises = Object.entries(this.accountsConfig).map(async ([accountId, config]) => { if (!config.appId || !config.appSecret) return null; try { const token = await this._getTenantAccessToken(config.appId, config.appSecret); if (!token) return null; const res = await fetch('https://open.feishu.cn/open-apis/bot/v3/info', { headers: { 'Authorization': `Bearer ${token}` } }); const data = await res.json(); if (data.code === 0 && data.bot) { return { name: data.bot.app_name, open_id: data.bot.open_id, appId: config.appId, accountId: accountId }; } } catch (e) { log('ERROR', `Failed to fetch bot info for ${accountId}:`, e.message); } return null; }); ``` ```javascript async _getTenantAccessToken(appId, appSecret) { const cache = this.tokenCache.get(appId); if (cache && Date.now() < cache.expireTime) { return cache.token; } try { const response = await fetch('https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal', { method: 'POST', headers: { 'Content-Type': 'application/json; charset=utf-8' }, body: JSON.stringify({ app_id: appId, app_secret: appSecret }) }); const data = await response.json(); if (data.code === 0) { const expireTime = Date.now() + (data.expire - 300) * 1000; this.tokenCache.set(appId, { token: data.tenant_access_token, expireTime }); return data.tenant_acc ...[truncated 2415 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove `_ensureBotInfos()` from the constructor so importing the package has no network side effects. 2. Initialize bot data lazily only after `resolve()` is called. 3. Authenticate only the account selected by the supplied `accountId`. 4. Avoid authenticating every configured bot account for discovery. Prefer a non-secret local mapping of account IDs to known OpenIDs, or retrieve one specifically mentioned bot on demand. 5. Separate configuration parsing from secret access so metadata-only operations do not retain all `appSecret` values in the resolver instance. 6. Document all network requests and identify exactly which account credentials each operation uses. 7. Add tests asserting that module import performs no network requests and that resolving through one account does not authenticate unrelated accounts. ]]>
