Back to skill

Security audit

Feishu Identity Routing

Security checks for vulnerabilities and agentic risk

Overview

The skill is purpose-aligned, but it persistently stores and mutates sensitive Feishu identity-routing data without enough privacy, validation, or file-scope safeguards.

Install only in a controlled workspace where Feishu identity correlation is authorized. Restrict access to the identity directory, avoid running batch imports on untrusted paths, treat logs as sensitive, and review or harden the scripts before using the master file for outbound messages.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/merge_feishu_identity_batch.js:14
Finding

Unrestricted Batch Input Path Enables Disclosure of Arbitrary JSON Files

Content
View full analysis

Vulnerability Details

File Location: scripts/merge_feishu_identity_batch.js:14-21, 24-43
Vulnerability Type: Unrestricted local file read and sensitive output exposure
Risk Level: Medium

Vulnerable Code

javascript
const inputPath = process.argv[2];
if (!inputPath) fail('Usage: node bin/merge_feishu_identity_batch.js <json-file>');

const resolved = path.isAbsolute(inputPath) ? inputPath : path.join(workspace, inputPath);
if (!fs.existsSync(resolved)) fail(`Input file not found: ${resolved}`);

const raw = fs.readFileSync(resolved, 'utf8');
const records = JSON.parse(raw);
if (!Array.isArray(records)) fail('Input JSON must be an array of records');

const results = [];
for (const rec of records) {
  const out = spawnSync(process.execPath, [singleScript, JSON.stringify(rec)], { encoding: 'utf8' });
  results.push({
    record: rec,
    code: out.status,
    stdout: out.stdout.trim(),
    stderr: out.stderr.trim()
  });
}

const summary = {
  ok: results.every(r => r.code === 0),
  total: results.length,
  succeeded: results.filter(r => r.code === 0).length,
  failed: results.filter(r => r.code !== 0).length,
  results
};

console.log(JSON.stringify(summary, null, 2));
if (!summary.ok) process.exit(2);

Technical Analysis

The batch script accepts an input path directly from the command line. Absolute paths are used without restriction, while relative paths are joined to the workspace without canonicalization or containment validation. The script does not require the resolved file to remain within an authorized import directory and does not reject symbolic links that resolve outside that directory.

Once parsed, every element of a JSON array is copied into the results structure and printed to standard output as record, regardless of whether the downstream merge operation succeeds. Consequently, any readable file containing a top-level JSON arra ...[truncated 1532 chars]

Remediation
View remediation

Remediation Suggestions

  • Restrict imports to a dedicated directory under the workspace.
  • Resolve both the approved directory and requested file with fs.realpathSync() before reading.
  • Verify that the canonical file path is equal to, or is a descendant of, the approved directory using path.relative().
  • Reject absolute paths unless they are explicitly required and pass the same containment check.
  • Reject symbolic links or verify their final canonical destination.
  • Validate the complete input array before invoking the single-record script.
  • Do not include raw input records in the summary. Return only record indexes, status codes, and redacted validation errors.
  • Run the script under a dedicated operating-system account with access only to the identity workspace.

Example containment logic:

javascript
const importRoot = fs.realpathSync(path.join(workspace, 'identity', 'imports'));
const candidate = fs.realpathSync(path.resolve(importRoot, inputPath));
const relative = path.relative(importRoot, candidate);

if (relative.startsWith('..') || path.isAbsolute(relative)) {
  fail('Input file must be inside the approved import directory');
}

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/merge_feishu_identity_batch.js:24
Finding

Feishu Identity Records Containing PII Are Logged and Stored Without Explicit File Protection

Content
View full analysis

Vulnerability Details

File Locations: scripts/merge_feishu_identity_batch.js:24-43, scripts/review_feishu_pending.js:13-18, 30-35, scripts/merge_feishu_identity.js:13-18
Vulnerability Type: Plaintext sensitive-data exposure and non-atomic file storage
Risk Level: Medium

Vulnerable Code

From scripts/merge_feishu_identity_batch.js:

javascript
const results = [];
for (const rec of records) {
  const out = spawnSync(process.execPath, [singleScript, JSON.stringify(rec)], { encoding: 'utf8' });
  results.push({
    record: rec,
    code: out.status,
    stdout: out.stdout.trim(),
    stderr: out.stderr.trim()
  });
}

const summary = {
  ok: results.every(r => r.code === 0),
  total: results.length,
  succeeded: results.filter(r => r.code === 0).length,
  failed: results.filter(r => r.code !== 0).length,
  results
};

console.log(JSON.stringify(summary, null, 2));

From scripts/review_feishu_pending.js:

javascript
function load() {
  return JSON.parse(fs.readFileSync(masterPath, 'utf8'));
}

function save(data) {
  fs.writeFileSync(masterPath, JSON.stringify(data, null, 2) + '\n');
}

const data = load();
data.pending_reviews = data.pending_reviews || [];

if (action === 'list') {
  console.log(JSON.stringify({ count: data.pending_reviews.length, pending_reviews: data.pending_reviews }, null, 2));
  process.exit(0);
}

From scripts/merge_feishu_identity.js:

javascript
function loadJson(p) {
  return JSON.parse(fs.readFileSync(p, 'utf8'));
}

function saveJson(p, data) {
  fs.writeFileSync(p, JSON.stringify(data, null, 2) + '\n', 'utf8');
}

Technical Analysis

Identity records contain names, phone numbers, Feishu open_id values, user_id values, union_id values, and source metadata. The batch script includes complete records in its output, and the review script prints every pending record. Stan ...[truncated 2206 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove complete identity records from normal command output.
  • Return only non-sensitive record indexes, actions, and status codes.
  • Redact phone numbers and stable identifiers when diagnostic output is necessary.
  • Make full pending-review output an explicit privileged operation rather than the default list behavior.
  • Create the master and temporary files with mode 0600.
  • Verify that existing files are owned by the expected operating-system account and are not group- or world-readable.
  • Store the identity directory with mode 0700.
  • Write updates atomically:
    1. Create a unique temporary file in the same directory with exclusive creation and mode 0600.
    2. Write and flush the complete JSON document.
    3. Optionally call fsync() on the file and containing directory where durability is required.
    4. Rename the temporary file over the destination.
    5. Clean up temporary files on failure.
  • Add file locking or use a transactional data store if concurrent writers are possible.
  • Configure log retention and access controls appropriate for personal data.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/merge_feishu_identity.js:21
Finding

Unauthenticated Identity Submissions Can Poison Confirmed Outbound Routing Records

Content
View full analysis

Vulnerability Details

File Location: scripts/merge_feishu_identity.js:21-50, 57-64, 89-129
Vulnerability Type: Insufficient provenance validation and routing-record conflict handling
Risk Level: Medium

Vulnerable Code

The normalization logic accepts caller-controlled provenance and status values, with unverified records defaulting to confirmed:

javascript
function normalizeRecord(input) {
  const required = ['source_agent', 'app_context', 'open_id', 'user_id', 'union_id', 'name', 'phone', 'captured_at'];
  for (const key of required) {
    if (!input[key] || typeof input[key] !== 'string') {
      fail(`Missing required field: ${key}`);
    }
  }
  return {
    source_agent: input.source_agent.trim(),
    app_context: input.app_context.trim(),
    open_id: input.open_id.trim(),
    user_id: input.user_id.trim(),
    union_id: input.union_id.trim(),
    name: input.name.trim(),
    phone: input.phone.trim(),
    captured_at: input.captured_at.trim(),
    source_type: input.source_type ? String(input.source_type).trim() : 'agent-reported',
    status: input.status ? String(input.status).trim() : 'confirmed'
  };
}

function findSubject(master, rec) {
  let subject = master.subjects.find(s => s.identifiers?.union_id === rec.union_id);
  if (subject) return { subject, mode: 'union_id' };

  const byUserId = master.subjects.filter(s => Array.isArray(s.identifiers?.user_ids) && s.identifiers.user_ids.includes(rec.user_id));
  if (byUserId.length === 1) return { subject: byUserId[0], mode: 'user_id' };
  if (byUserId.length > 1) return { subject: null, mode: 'ambiguous_user_id' };

  return { subject: null, mode: 'new_subject' };
}

App identities are matched by both app context and open ID, allowing a second open ID for the same app context to be inserted rather than treated as a conflict:

javascript
function upsertAppIdentity(subject, rec)
...[truncated 4743 chars]
Remediation
View remediation

Remediation Suggestions

  • Authenticate every submission source rather than trusting the source_agent field supplied inside the record.
  • Derive source identity and trust level from the invocation context, not from caller-controlled JSON.
  • Ignore caller-supplied status values. New records should default to unverified or needs_review unless confirmed through an authenticated provider lookup.
  • Verify the relationship among union_id, user_id, open_id, and app context using the corresponding Feishu account or another trusted identity source.
  • Enforce an explicit invariant for active routing identities, such as one confirmed open_id per subject and app context.
  • If the same app context is submitted with a different open ID, create a pending review instead of appending or replacing the identity.
  • Send conflicting union IDs, user IDs, phone numbers, and other strong identity discrepancies to pending review.
  • Define and enforce allowlists for valid app contexts, source agents, source types, and status values.
  • Add schema validation, maximum field lengths, identifier format checks, and timestamp validation.
  • Record immutable audit metadata for every accepted change, including authenticated actor, previous value, new value, decision source, and timestamp.
  • Require explicit approval before routing components consume newly observed identities.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (7)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 41)May include surrounding context.

md
- `scripts/merge_feishu_identity.js` — merge one record

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 43)May include surrounding context.

md
- `scripts/review_feishu_pending.js` — approve/reject pending merges

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill directs creation of persistent workspace files that store cross-app identity mappings linking Feishu identifiers across contexts, but it provides no warning, consent requirement, data-minimization guidance, or retention/privacy controls. This can lead to unnecessary collection and persistence of sensitive identity correlation data, increasing privacy, compliance, and misuse risk in a multi-agent environment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code performs a direct file write to a persistent identity store containing personal data such as name and phone number. The operation has no confirmation prompt, warning message, or explanatory comment/disclosure to inform the user that running the script will modify stored identity records.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This code writes directly to the Feishu user master JSON file, and later approve/reject paths remove items from pending_reviews and persist those changes. Although the script prints a result after completion, there is no pre-action confirmation, warning comment/docstring, or other disclosure that these operations mutate identity records and review state.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This markdown file demonstrates resolving a human identity and sending an outbound DM from a specific account, including a concrete target identifier. The example omits any warning that it involves contacting a user and handling identity-linked data, which is user-impacting behavior covered by markdown warning requirements.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The skill description emphasizes building and using a reusable Feishu identity master and setting up merge/review workflows, but this file's review operation directly mutates the master record store by merging identities, appending identifiers, phones, aliases, notes, and moving records between queues. While related to review, this is more than a passive review utility and performs authoritative state changes to the master identity graph.

Content

No source excerpt is available for this finding.