Back to skill

Security audit

Ora决策人开发专家

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it sends company/contact lookups to an external API and leaves raw contact results in a predictable shared temp folder.

Install only if you are comfortable using a local OraAgent.key for Topeasy API queries and handling business/contact data under applicable privacy, anti-spam, and commercial communication rules. Run it only for authorized lookups, and delete or protect files written under the ora-contact-pro temp directory after use, especially on shared machines.

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

Warning
Location
scripts/search_by_company_name.js:32
Finding

Company lookup results are stored in an insecure shared temporary directory

Content
View full analysis

Vulnerability Details

File Location: scripts/search_by_company_name.js, lines 32-41
Vulnerability Type: Predictable and insufficiently protected temporary-file storage
Risk Level: Medium

Vulnerable Code

js
function saveResult(companyName, json) {
  const ts = new Date().toISOString().slice(0, 19).replace(/[:-]/g, "");
  const safeName = companyName.replace(/[^a-zA-Z0-9\u4e00-\u9fa5_-]/g, "_").slice(0, 60) || "company";
  const fileName = `easy_search_company_name_${safeName}_${ts}.json`;
  const outputDir = path.join(os.tmpdir(), "ora-contact-pro");
  fs.mkdirSync(outputDir, { recursive: true });
  const filePath = path.join(outputDir, fileName);
  fs.writeFileSync(filePath, JSON.stringify(json, null, 2), "utf-8");
  return { fileName, filePath };
}

Technical Analysis

The function writes the complete company lookup response into a fixed directory under the operating system's shared temporary location. It does not specify restrictive permissions for either the directory or the result file, so effective permissions depend on the process umask and host configuration. On common configurations, the resulting JSON file may be readable by other local users.

The directory name and result filename are predictable from the supplied company name and the current timestamp. The implementation also uses normal file creation rather than exclusive creation and does not verify whether the directory or destination is controlled through symbolic links. If an attacker can pre-create the fixed temporary directory or a predicted destination entry, the write may be redirected.

The stored response may contain employee names, email addresses, telephone numbers, physical addresses, social-media profiles, and other contact information.

Attack Path

  1. A local attacker identifies the fixed /tmp/ora-contact-pro output location.
  2. The attacker monitors that directory or pre-creates it before the victi ...[truncated 1156 chars]
Remediation
View remediation

Remediation Suggestions

  • Create a unique private directory for every invocation with fs.mkdtempSync() rather than reusing a fixed shared directory.
  • Set the output directory mode to 0700 and each result file to 0600.
  • Open the result with exclusive creation, such as flag: "wx", to prevent overwriting an existing destination.
  • Use fs.lstatSync() or secure descriptor-based operations to reject symbolic links and unexpected filesystem object types.
  • Verify that the created directory is owned by the current user.
  • Remove the result file and per-run directory immediately after the Agent consumes the response.
  • Where practical, avoid persistent storage entirely and return the parsed response through standard output or an authenticated in-memory channel.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/search_by_domain.js:60
Finding

Domain lookup results are stored in an insecure shared temporary directory

Content
View full analysis

Vulnerability Details

File Location: scripts/search_by_domain.js, lines 60-72
Vulnerability Type: Predictable and insufficiently protected temporary-file storage
Risk Level: Medium

Vulnerable Code

js
function saveResult(domains, json) {
  const ts = new Date().toISOString().slice(0, 19).replace(/[:-]/g, "");
  const label = domains.length === 1
    ? domains[0].replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 60)
    : `${domains.length}_domains`;
  const fileName = `easy_search_domains_${label || "domains"}_${ts}.json`;
  const outputDir = path.join(os.tmpdir(), "ora-contact-pro");
  fs.mkdirSync(outputDir, { recursive: true });
  const filePath = path.join(outputDir, fileName);
  fs.writeFileSync(filePath, JSON.stringify(json, null, 2), "utf-8");
  return { fileName, filePath };
}

Technical Analysis

Batch domain-search responses are saved under the fixed shared temporary path /tmp/ora-contact-pro without explicit directory or file permissions. Effective access restrictions therefore depend on the process umask. The implementation does not perform ownership checks, symbolic-link checks, exclusive file creation, or automatic cleanup.

Filenames are derived from the queried domain or the number of domains and a timestamp with one-second precision. This makes the destination substantially predictable to a local attacker who knows or observes when a lookup occurs. Batch responses can increase the severity of disclosure because one file may contain records for multiple companies and their employees.

Attack Path

  1. A local attacker identifies or pre-creates the fixed /tmp/ora-contact-pro directory.
  2. The victim starts a single-domain or batch-domain lookup.
  3. The script writes the complete response to a predictable filename under that directory.
  4. If host permissions permit, the attacker reads the result and obtains data for every queried domain.
  5. If the attacker control ...[truncated 864 chars]
Remediation
View remediation

Remediation Suggestions

  • Replace the shared output directory with a per-invocation directory created through fs.mkdtempSync().
  • Enforce mode 0700 on the private directory and mode 0600 on the result file.
  • Create files atomically and exclusively with flag: "wx".
  • Validate ownership and use lstat checks to reject symbolic links or other unexpected destination types.
  • Generate filenames with cryptographically random components rather than query values and timestamps.
  • Delete saved domain results as soon as they have been consumed, including cleanup in error and interruption paths.
  • Consider streaming the response directly to the trusted consumer instead of persisting sensitive batch data.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/search_by_linkedin.js:40
Finding

LinkedIn lookup results are stored in an insecure shared temporary directory

Content
View full analysis

Vulnerability Details

File Location: scripts/search_by_linkedin.js, lines 40-49
Vulnerability Type: Predictable and insufficiently protected temporary-file storage
Risk Level: Medium

Vulnerable Code

js
function saveResult(universalName, json) {
  const ts = new Date().toISOString().slice(0, 19).replace(/[:-]/g, "");
  const safeName = universalName.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 60) || "linkedin";
  const fileName = `easy_search_linkedin_${safeName}_${ts}.json`;
  const outputDir = path.join(os.tmpdir(), "ora-contact-pro");
  fs.mkdirSync(outputDir, { recursive: true });
  const filePath = path.join(outputDir, fileName);
  fs.writeFileSync(filePath, JSON.stringify(json, null, 2), "utf-8");
  return { fileName, filePath };
}

Technical Analysis

LinkedIn company-search responses are written to the same static temporary directory as the other lookup modes. Neither the directory nor the file is assigned a restrictive mode. There is no check that the directory is owned by the current user, no protection against pre-existing symbolic links, and no exclusive creation option.

The filename incorporates the public LinkedIn company identifier and a timestamp, making it predictable. Since the saved data may include individual employee records and direct contact details, persistent storage under a potentially accessible temporary path creates a confidentiality risk.

Attack Path

  1. A local attacker discovers the fixed temporary directory used by the Skill.
  2. The attacker waits for a lookup or pre-creates the directory before execution.
  3. The victim queries a LinkedIn company identifier.
  4. The script writes the returned JSON data using a predictable filename and default permissions.
  5. The attacker reads the residual file or, where the attacker controls the directory, redirects the write through a pre-created symbolic link.
  6. The absence of cleanup extends the period du ...[truncated 656 chars]
Remediation
View remediation

Remediation Suggestions

  • Use fs.mkdtempSync() to create a private and unpredictable output directory for each run.
  • Explicitly set directory permissions to 0700 and result-file permissions to 0600.
  • Use exclusive file creation with flag: "wx" and fail safely if the destination already exists.
  • Confirm directory ownership and reject symbolic links through lstat or secure file-descriptor operations.
  • Add random filename components generated with Node.js crypto APIs.
  • Implement reliable cleanup after successful consumption and in finally or termination handling.
  • Prefer an in-memory transfer mechanism when persistent copies of contact data are unnecessary.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (10)

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding

Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| 企业名称 | `node ./scripts/search_by_company_name.js "<企业名称>"` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| 企业名称 | `node ./scripts/search_by_company_name.js "<企业名称>"` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| 一个或多个企业域名 | `node ./scripts/search_by_domain.js "<域名1>" "<域名2>" ...` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| LinkedIn 企业号 | `node ./scripts/search_by_linkedin.js "<LinkedIn企业号或公司链接>"` |

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill invokes external Node.js scripts that read a local key file and send user-supplied queries to an external service, but it declares no explicit tool scope or permissions. That creates a real security and governance gap because reviewers and runtime policy may not have a clear, enforceable declaration of network/file access, increasing the chance of unintended data disclosure or execution in contexts that did not expect such capabilities.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The file's natural-language description and usage text are entirely in Chinese, and the runtime prompts and status messages are also Chinese-only. This imposes a specific language on users without any opt-in, fallback, or stated justification that the skill is intended only for a Chinese-speaking or region-specific context.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This code file contains natural-language documentation and runtime messages entirely in Chinese, including usage instructions. Under the policy, forcing a specific language without user opt-in is a locale-policy violation unless the constraint is explicitly justified, which is not present here.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script writes the full API response to a predictable local temporary directory, which can persist sensitive company/contact data beyond the immediate query. In a skill explicitly dealing with decision-maker and contact information, local persistence increases the risk of unintended disclosure to other local users, processes, backups, or later reuse outside the user's original authorization scope.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

该技能的 stated purpose 是合规查询企业及决策人信息,联网查询本身是合理的,但代码还会主动在固定路径读取 OraAgent.key 作为认证凭据。凭据文件访问不是从技能用途自然推导出的用户可见能力,且可能让调用者在不知情时使用本地敏感配置。

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.