T09 · Insecure Skill Coding Practices
- Location
index.js:30- Finding
Feishu Minutes Token Disclosure Through Process Logs
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill does what it says, but it handles Feishu credentials and meeting tokens in ways users should review before installing.
Install only if you are comfortable with local storage of Feishu meeting transcripts, recordings, and a cached tenant access token. Run it in a private workspace, protect or clear the memory directory, avoid shared logs, and prefer tightly scoped Feishu app credentials.
index.js:30Feishu Minutes Token Disclosure Through Process Logs
lib/auth.js:114Tenant Access Token Stored in a Plaintext Cache File Without Explicitly Restricted Permissions
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
const fs = require('fs');
const path = require('path');
// Robust .env loading
const possibleEnvPaths = [
path.resolve(process.cwd(), '.env'),
path.resolve(__dirname, '../../../.env'),
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
// Robust .env loading
const possibleEnvPaths = [
path.resolve(process.cwd(), '.env'),
path.resolve(__dirname, '../../../.env'),
path.resolve(__dirname, '../../../../.env')
];
The code searches parent directories for .env files, which can cause secrets to be loaded from unexpected locations outside the application's intended boundary. In shared or multi-project environments, this increases the risk of accidentally ingesting the wrong credentials or attacker-influenced configuration, leading to confused-deputy behavior or unauthorized external authentication attempts.
// Robust .env loading
const possibleEnvPaths = [
path.resolve(process.cwd(), '.env'),
path.resolve(__dirname, '../../../.env'),
path.resolve(__dirname, '../../../../.env')
];
Searching even higher-level parent directories for .env files broadens the trust boundary further and makes configuration source ambiguity worse. If a higher-level .env is present or writable by another actor, the application may silently consume unintended secrets and then use them to request tokens from the external API.
const possibleEnvPaths = [
path.resolve(process.cwd(), '.env'),
path.resolve(__dirname, '../../../.env'),
path.resolve(__dirname, '../../../../.env')
];
let envLoaded = false;
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.
}
}
// Force Refresh: Delete memory cache and file cache
if (forceRefresh) {
tokenCache.token = null;
tokenCache.expireTime = 0;
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
}
}
throw lastError || new Error("Failed to retrieve access token after retries");
}
module.exports = {
The skill explicitly states it fetches transcripts and media and stores them as local files, but it does not warn that these artifacts may contain highly sensitive meeting content, personal data, or confidential business information. This omission can cause users to run the skill in insecure environments, unintentionally retain sensitive data on disk, or mishandle exported recordings and transcripts.
This code reads sensitive credentials from environment variables and config files, then uses them to obtain an access token and persists that token to disk. While there is some error logging, there is no visible comment, prompt, or user-facing disclosure warning that credentials are being consumed and that authentication tokens will be stored in a local cache file.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
let lastError;
for (let attempt = 1; attempt <= 3; attempt++) {
try {
const response = await fetch('https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
This is a manifest file, so vague-trigger checks apply. The description states the skill can 'Fetch info, subtitle, and media from Feishu Minutes (Yi)' but provides no specific activation phrases, constraints, or exclusion conditions, making invocation scope ambiguous.
Using a caret version for dotenv allows future compatible releases within the major version range to be installed, which can introduce unexpected supply-chain changes over time. While not immediately exploitable from this file alone, looser dependency pinning increases the chance of pulling a compromised or breaking upstream release in future installs.
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"dotenv": "^16.3.1",
"node-fetch": "^2.7.0",
"yargs": "^17.7.2"
}
Using a caret version for node-fetch permits automatic uptake of later minor or patch releases, which expands supply-chain exposure if an upstream package is compromised or introduces unsafe behavior. The risk is contextual and indirect, but exact pinning is a common hardening measure for reproducible and safer builds.
},
"dependencies": {
"dotenv": "^16.3.1",
"node-fetch": "^2.7.0",
"yargs": "^17.7.2"
}
}
Using a caret version for yargs means installations may resolve to different package contents over time, creating a supply-chain risk window if a later allowed release is malicious or flawed. This does not indicate active compromise here, but it is a real hardening weakness for dependency control.
"dependencies": {
"dotenv": "^16.3.1",
"node-fetch": "^2.7.0",
"yargs": "^17.7.2"
}
}