Back to skill

Security audit

FTTR Operator Copilot

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its FTTR operator-support purpose, but it needs review because it can send an operator bearer token and device data to any configured HTTP or HTTPS endpoint.

Install only if you trust the publisher and can control the runtime environment. Keep FTTRAI_RPC_URL on the official HTTPS endpoint or a trusted internal endpoint, never HTTP, and treat FTTRAI_OPERATOR_AUTH_TOKEN as a privileged secret. Use mark_alerts_as_read only after confirming the alert IDs, because it changes FTTRAI state.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
src/config.js:4
Finding
Operator bearer token can be transmitted to an arbitrary or plaintext endpoint<![CDATA[ ## Vulnerability Details **File Location**: `src/config.js:4-29`, `src/connect.js:9-25` **Vulnerability Type**: Arbitrary credential destination and plaintext transmission **Risk Level**: High ### Vulnerable Code From `src/config.js`: ```js export function loadConfig(env = process.env) { const baseUrl = trimTrailingSlash(env.FTTRAI_RPC_URL || DEFAULT_FTTRAI_RPC_URL); const token = env.FTTRAI_OPERATOR_AUTH_TOKEN || ""; const timeoutMs = parsePositiveInt(env.FTTRAI_TIMEOUT_MS, 30000); const maxRetries = parsePositiveInt(env.FTTRAI_MAX_RETRIES, 2); const missing = []; if (!token) missing.push("FTTRAI_OPERATOR_AUTH_TOKEN"); if (missing.length > 0) { const err = new Error(`缺少必要环境变量: ${missing.join(", ")}`); err.code = "missing_config"; throw err; } let parsedUrl; try { parsedUrl = new URL(baseUrl); } catch { const err = new Error("FTTRAI_RPC_URL 不是有效 URL"); err.code = "invalid_config"; throw err; } if (!["http:", "https:"].includes(parsedUrl.protocol)) { const err = new Error("FTTRAI_RPC_URL 只支持 http 或 https"); err.code = "invalid_config"; throw err; } ``` From `src/connect.js`: ```js async unary(procedure, body = {}) { const url = `${this.config.baseUrl}${procedure}`; let lastError; for (let attempt = 0; attempt <= this.config.maxRetries; attempt += 1) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs); try { const response = await fetch(url, { method: "POST", headers: { "Authorization": `Bearer ${this.config.token}`, "Content-Type": "application/json", "Accept": "application/json", }, body: JSON.stringify(body), signal: controller.signal, }); ``` ### Technical Analysis The configurable `FTTRAI_RPC_URL` is validated only as an HTTP or HTTPS URL. No check requires TLS, restrict ...[truncated 2497 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require HTTPS for normal operation: ```js if (parsedUrl.protocol !== "https:") { const err = new Error("FTTRAI_RPC_URL must use HTTPS"); err.code = "invalid_config"; throw err; } ``` 2. Allowlist the documented production hostname, such as `fms-main.fttrai.com`, unless a clearly labeled development mode is enabled. 3. Do not permit production operator credentials to be used with custom endpoints. Use separate, restricted development credentials for test or private deployments. 4. Reject unexpected URL components, including embedded usernames or passwords, fragments, and unauthorized ports. 5. Consider constructing RPC URLs with `new URL(procedure, baseUrl)` and validate the final URL immediately before transmission. 6. Configure an explicit redirect policy, such as `redirect: "error"`, or validate every redirect destination before forwarding credentials. 7. Add automated tests confirming rejection of: - Plaintext HTTP endpoints. - Untrusted hosts. - Embedded URL credentials. - Unexpected ports. - Redirects to different origins. 8. Rotate the operator token if it may previously have been used with an untrusted or plaintext endpoint. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
src/tools/diagnostics.js:5
Finding
Offline diagnosis retrieves alerts unrelated to the selected device<![CDATA[ ## Vulnerability Details **File Location**: `src/tools/diagnostics.js:5-9` **Vulnerability Type**: Excessive data access and failure to apply available server-side scope **Risk Level**: Medium ### Vulnerable Code ```js export async function diagnoseDeviceOffline(client, args = {}) { const deviceIdentifier = requiredString(args.device_identifier, "device_identifier"); const detail = await getDeviceDetail(client, { device_identifier: deviceIdentifier }); const alerts = await listDeviceAlerts(client, { event_type: "ALERT", limit: args.alert_limit || 10 }); const relatedAlerts = filterAlertsForDevice(alerts.data.alerts, detail.data.detail); ``` ### Technical Analysis The workflow is initiated for one explicit `device_identifier`, but its alert query does not pass that identifier to `listDeviceAlerts()`. The resulting request therefore retrieves a recent alert list across the operator token's accessible scope and filters it locally afterward. This broader request is unnecessary because `listDeviceAlerts()` already supports a `device_identifier` parameter and resolves it into a device MAC filter before calling the backend. Applying the filter at the server would limit returned data to the selected device. Although the final diagnostic response includes only `relatedAlerts`, unrelated records are still returned by the backend and processed in Skill memory. Those records can contain alert messages, event details, device IDs, MAC addresses, and operational metadata. The fixed default limit of ten reduces the amount of data but does not enforce least privilege. The implementation can also produce incomplete diagnosis results: if the latest operator-wide alerts belong to other devices, relevant alerts for the selected device may not appear within the limited response. ### Attack Path 1. A user or agent invokes `diagnose_device_offline` with a particular device identifier. 2. The Skill retrieves that device's details. 3. The Skill calls `listDevi ...[truncated 1131 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Pass the selected device identifier into the alert query so that filtering occurs at the backend: ```js export async function diagnoseDeviceOffline(client, args = {}) { const deviceIdentifier = requiredString(args.device_identifier, "device_identifier"); const detail = await getDeviceDetail(client, { device_identifier: deviceIdentifier, }); const alerts = await listDeviceAlerts(client, { device_identifier: deviceIdentifier, event_type: "ALERT", limit: args.alert_limit || 10, }); const relatedAlerts = filterAlertsForDevice( alerts.data.alerts, detail.data.detail, ); ``` Additional hardening measures: 1. Retain local filtering as defense in depth, but do not use it as a substitute for server-side scoping. 2. Add a test asserting that `diagnoseDeviceOffline()` sends the resolved device MAC in the `ListAlerts` request. 3. Add a test with alerts from multiple devices and verify that the backend request is scoped before any unrelated records are returned. 4. Validate and cap `alert_limit` explicitly in the diagnostic function or consistently rely on the existing list-query limit control. 5. Avoid retaining raw, unfiltered API responses longer than necessary. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (23)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description describes a network/API integration skill with multiple FTTR Copilot operational capabilities. The supplied code chunk does not implement any of those behaviors; it only defines generic utility functions for formatting success/failure results and emitting JSON output. This is a materially different primary purpose, so the description does not accurately represent the actual code behavior.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill declares access to environment variables and relies on networked API calls, but it does not define any explicit tool scope such as permissions or allowed-tools. In a capability-bearing skill, missing scope boundaries can allow broader-than-necessary execution or make review and enforcement of least privilege difficult.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The instruction "Keep answers in Chinese unless the user asks otherwise" imposes a default language choice rather than offering a neutral user-language selection. This matches the policy category for language/locale constraints because the skill forces a specific language unless the user actively opts out.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The example shows a state-changing operation that marks alerts as read without any warning that it mutates operational data and may hide or suppress visibility of active incidents for other operators. In an operator cloud-control context, normalizing this command as a copy-paste example increases the chance of accidental execution against production alerts, reducing incident awareness and audit clarity.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The error message `未知工具` is hard-coded in Chinese, and the CLI also presents a mixed-language interface without offering the user any language selection or opt-in. This is a natural-language policy issue because the skill forces a specific language/locale in user-facing text rather than allowing user choice or clearly documenting a justified locale restriction.

Natural-Language Policy Violations

Medium
Confidence
71% confidence
Finding
Although most help text is in English, this CLI is branded for a specific copilot context and elsewhere uses Chinese-only user-facing messaging, with no documented locale selection. The absence of any user language choice means the skill's interface behavior is not compliant with the requirement to avoid forcing a specific language/locale without opt-in.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The thrown error strings are written only in Chinese, which imposes a specific language on users regardless of their locale or preferences. Under the natural-language policy rules, forcing a language without offering choice or documenting a justified locale constraint is a policy violation.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This code performs an HTTP POST and includes a bearer token plus a JSON-serialized body, which may transmit user or system data to a remote service. In this file there is no confirmation prompt, logging, comment, or docstring disclosing that sensitive data and credentials are sent over the network.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This code returns Chinese-language titles, summaries, suggestions, and error messages directly in multiple user-facing responses, which enforces a specific language/locale by default. The file does not offer any mechanism for user language selection or document a justified locale restriction.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This code returns user-facing titles, summaries, suggestions, and error messages in Chinese only, such as "Operator 设备列表" and other Chinese guidance text. The file does not provide any locale selection, fallback, or opt-in mechanism, which is a natural-language locale policy concern under the stated rules.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
Beyond the list operation, the file continues to hard-code Chinese-only summaries, suggestions, and validation errors, including device status text and the "不能为空" error. Because these messages are embedded across multiple functions with no visible opt-in or locale choice, the skill enforces a single language for all users.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
This code file contains user-facing natural-language output that is consistently forced to Chinese, including titles, summaries, suggestions, and examples. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is explicitly justified.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
This code performs multiple remote client.unary calls to retrieve network topology, station statistics, and device details, and includes raw responses plus device identifiers/MAC-related data in returned objects. The file contains no confirmation prompt, logging, comment, or docstring warning that network/device data will be queried and exposed, which meets the missing-warning criteria for code files.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
User-facing titles, summaries, suggestions, and error messages throughout the file are written in Chinese, forcing a specific locale in responses. Under the policy, a fixed language is a violation unless the skill offers user choice or the locale restriction is clearly documented and justified.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This code returns hardcoded Chinese titles and summaries, which imposes a specific language on users. The policy allows locale constraints only when users can opt in or when the regional limitation is clearly documented and justified, neither of which is evident in this file.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The fault statistics response includes Chinese-only titles and summaries. This is a natural-language policy issue because the skill appears to require a specific language rather than allowing user choice or declaring a justified region-specific constraint.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The returned title, summary, and suggestions are all Chinese-only, which forces one locale for end users. Under the stated policy, this should either be optional for users or clearly documented as a justified regional specialization.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The usage examples hard-code Chinese natural-language inputs (`卧室网慢`, `排查区域离线`) in a way that implies a specific language expectation. There is no accompanying note that other languages are supported, nor any justification that the skill is intentionally limited to a Chinese locale.

Missing User Warnings

Low
Confidence
84% confidence
Finding
This code file documents use of the required environment variable `FTTRAI_OPERATOR_AUTH_TOKEN`, which is a sensitive credential, but provides no warning or disclosure about secure handling, exposure risks, or that the CLI will use it to authenticate outbound requests. Under the code-file criteria, access to sensitive environment variables should have some visible disclosure via comments, logs, prompts, or accompanying documentation.

Missing User Warnings

Low
Confidence
84% confidence
Finding
This code reads the FTTRAI_OPERATOR_AUTH_TOKEN credential from the environment, which is a sensitive operation under the warning criteria for code files. The file contains no user-facing log, comment, or docstring explaining that credentials will be accessed, so users reviewing only this code would not receive disclosure here.

Natural-Language Policy Violations

Low
Confidence
97% confidence
Finding
The error messages "RPC 调用失败", "RPC 调用超时", and related text are hard-coded in Chinese. This creates a locale-specific user experience without any visible opt-in, language selection, or justification that the skill is intended only for Chinese-speaking users.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
The fallback error message string "未知错误" is hardcoded in Chinese, which imposes a specific language on users without any visible opt-in or locale-selection mechanism. This is a natural-language policy concern because the file does not offer alternative language handling or document a justified locale restriction.

Natural-Language Policy Violations

Low
Confidence
87% confidence
Finding
Even though this is an error path, the message is still user-visible natural language and is fixed to Chinese. That can violate the language-choice policy when no opt-in or documented locale restriction exists.

Static analysis

No suspicious patterns detected.