Back to skill

Security audit

feishu-broadcast

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Feishu tenant-wide broadcast tool, but it combines mass messaging authority with unsafe command execution and weak local secret handling.

Install only in a controlled admin environment. Treat it as capable of messaging every Feishu tenant user, reading Feishu app credentials, caching a tenant token locally, and executing local shell commands from provided arguments. Prefer requiring dry-run/confirmation, fixing exec usage, restricting .env loading, and hardening token/temp-file storage before production use.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
index.js:65
Finding

Shell Command Injection Through Broadcast Arguments

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
index.js:62
Finding

Predictable and Insecure Temporary Message Files

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
lib/api.js:57
Finding

Feishu Tenant Access Token Stored in a Shared Plaintext Cache

Content
View full analysis
now + 60) { return saved.token; } } catch (e) {} } ``` ```js try { const cacheDir = path.dirname(TOKEN_CACHE_FILE); if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true }); fs.writeFileSync(TOKEN_CACHE_FILE, JSON.stringify({ token: data.tenant_access_token, expire: now + data.expire }, null, 2)); } catch (e) {} ``` ### Technical Analysis The Feishu tenant access token is persisted as plaintext JSON in a shared `memory` directory located outside the Skill directory. The code does not: - Set restrictive file permissions. - Validate the owner or permissions of an existing cache file. - Prevent symbolic-link traversal. - Use exclusive or atomic creation. - Encrypt the stored token. - Use a platform-managed credential store. A Feishu tenant access token is a bearer credential. Any party that obtains it can use the application permissions represented by that token until it expires or is revoked. The network transmission flagged by the static pre-scan is itself consistent with the declared functionality: ```js const res = await fetch('https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ app_id: APP_ID, app_secret: APP_SECRET }) }); ``` The application ID and secret are sent over ...[truncated 1592 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (16)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · lib/api.js (reported line 8)May include surrounding context.

js
// Shared Token Cache
const TOKEN_CACHE_FILE = path.resolve(__dirname, '../../../memory/feishu_token.json');

// Robust .env loading
const possibleEnvPaths = [
  path.resolve(process.cwd(), '.env'),
  path.resolve(__dirname, '../../../.env'),

Credential Access

High
Category
Privilege Escalation
Confidence
72% confidence
Finding

The code searches for .env files not only in the current working directory but also in parent directories relative to the skill. In a shared agent or multi-project environment, this can unintentionally ingest secrets from unrelated repositories or higher-level directories, expanding the blast radius of any misconfiguration and violating least surprise.

Content

Scanner excerpt · lib/api.js (reported line 10)May include surrounding context.

js
// Robust .env loading
const possibleEnvPaths = [
  path.resolve(process.cwd(), '.env'),
  path.resolve(__dirname, '../../../.env'),
  path.resolve(__dirname, '../../../../.env')
];

Credential Access

High
Category
Privilege Escalation
Confidence
72% confidence
Finding

Loading .env from ../../../.env broadens secret access beyond the local module boundary and may capture credentials from unrelated contexts. In skill ecosystems where code is reused or nested, this creates an avoidable risk of over-collecting credentials and using secrets the operator did not intend this skill to access.

Content

Scanner excerpt · lib/api.js (reported line 11)May include surrounding context.

js
// Robust .env loading
const possibleEnvPaths = [
  path.resolve(process.cwd(), '.env'),
  path.resolve(__dirname, '../../../.env'),
  path.resolve(__dirname, '../../../../.env')
];

Credential Access

High
Category
Privilege Escalation
Confidence
72% confidence
Finding

Searching even further up the directory tree for .env files increases the chance of accidental credential capture from parent workspaces or system-level project folders. In the context of an agent skill, this is more dangerous because the skill may run in environments with many adjacent projects and shared secrets, making unintended secret access more likely.

Content

Scanner excerpt · lib/api.js (reported line 12)May include surrounding context.

js
const possibleEnvPaths = [
  path.resolve(process.cwd(), '.env'),
  path.resolve(__dirname, '../../../.env'),
  path.resolve(__dirname, '../../../../.env')
];

let envLoaded = false;

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explicitly enables tenant-wide broadcasting to all Feishu users, but the documentation does not present a prominent warning, authorization requirement, approval step, or clear confirmation that the action is organization-wide and potentially disruptive. In the context of enterprise messaging, this can be abused for mass spam, phishing, social engineering, reputational harm, or operational disruption, especially because it supports rich text and media and dynamically targets all users.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The code fetches all users and broadcasts caller-supplied text and images to every account without any confirmation, recipient scoping, or explicit warning about mass transmission. In a messaging-integrated skill, this creates a real risk of accidental bulk disclosure, spam, or misuse of internal recipient data even if the feature is intentional.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill builds shell command strings with untrusted values such as targetId, title, and image path, then executes them via child_process.exec. Because exec invokes a shell, crafted input containing shell metacharacters can lead to command injection, and this skill iterates over all users, amplifying the blast radius of any abuse.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · lib/api.js (reported line 45)May include surrounding context.

js
if (!APP_ID || !APP_SECRET) throw new Error("FEISHU_APP_ID or FEISHU_APP_SECRET not found");

  const res = await fetch('https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ app_id: APP_ID, app_secret: APP_SECRET })

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The code persists a bearer access token to a shared JSON file under a predictable path without setting restrictive file permissions or informing the operator. If other local users, processes, or adjacent skills can read that file, they can reuse the token to access the Feishu tenant APIs until expiry.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The post message content is always constructed under the zh_cn locale key, which forces a specific language/locale behavior. There is no visible user opt-in, alternative locale selection, or documented justification for restricting output to Chinese.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

This is a manifest file, so vague-trigger rules apply. The description 'Broadcast messages to all Feishu users in the tenant' defines a very broad activation/use scope without any constraints, exclusions, or specificity about when the skill should be used, which could encourage unintended invocation for high-impact mass messaging.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

User-provided message text is written to a predictable local temporary file before sending. This can expose sensitive content to other local processes or leave data behind if execution fails before deletion, making confidentiality dependent on filesystem state and error handling.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The comments repeatedly state an intent to call the existing feishu-post skill via CLI for robustness or safety, but the implemented sendPost path constructs the payload locally and performs the HTTP POST itself. This is an active contradiction between the documented intent in comments and the actual implementation approach.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

Using a caret version for dotenv allows automatic adoption of newer minor/patch releases, which can introduce supply-chain risk or unexpected behavior changes if an upstream package is compromised or regresses. While common in Node.js projects, this weakens build reproducibility and increases exposure compared with fully pinned dependencies.

Content

Scanner excerpt · package.json (reported line 10)May include surrounding context.

json
"test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "dotenv": "^16.3.1",
    "node-fetch": "^2.7.0",
    "yargs": "^17.7.2"
  }

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

Using a caret version for node-fetch permits non-exact dependency resolution, increasing supply-chain exposure and reducing reproducibility of builds. If an upstream release is malicious or flawed, new installs may pull it in without explicit review.

Content

Scanner excerpt · package.json (reported line 11)May include surrounding context.

json
},
  "dependencies": {
    "dotenv": "^16.3.1",
    "node-fetch": "^2.7.0",
    "yargs": "^17.7.2"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

Using a caret version for yargs means future installs may resolve to different package contents than originally tested, creating a low-severity supply-chain and stability risk. This is especially relevant for CLI tooling because argument parsing behavior changes can affect how the skill is invoked.

Content

Scanner excerpt · package.json (reported line 12)May include surrounding context.

json
"dependencies": {
    "dotenv": "^16.3.1",
    "node-fetch": "^2.7.0",
    "yargs": "^17.7.2"
  }
}