Back to skill

Security audit

UnifyPort Device API

Security checks for vulnerabilities and agentic risk

Overview

The skill is a scoped UnifyPort API helper, but it can run high-impact live account actions and its confirmation tokens can be reused within their five-minute window.

Review this before installing if you expect strong technical one-time confirmation for live changes. Use documentation mode when possible, only provide UNIFYPORT_API_KEY for deliberate live calls, inspect every preview carefully, and avoid reusing confirmation tokens or retrying state-changing operations without checking the external result first.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/api-client.mjs:508
Finding

State-Changing Confirmation Tokens Can Be Replayed

Content
View full analysis

Vulnerability Details

File Location: scripts/api-client.mjs, lines 508–525 and 592–603
Vulnerability Type: Replayable confirmation token
Risk Level: Medium

Vulnerable Code

js
export const createConfirmationToken = (prepared, env = process.env, nowMs = Date.now()) => {
  const apiKey = getApiKey(env);
  const expiresAt = Math.floor(nowMs / 1000) + CONFIRMATION_TTL_SECONDS;
  return `${expiresAt}.${confirmationMac(prepared, apiKey, expiresAt)}`;
};

const verifyConfirmationToken = (actual, prepared, apiKey, nowMs) => {
  const match = /^(\d{10,12})\.([a-f0-9]{64})$/.exec(actual ?? '');
  if (!match) return 'confirmation_mismatch';
  const expiresAt = Number(match[1]);
  const now = Math.floor(nowMs / 1000);
  if (expiresAt < now) return 'confirmation_expired';
  if (expiresAt > now + CONFIRMATION_TTL_SECONDS) return 'confirmation_expiry_invalid';
  const expected = confirmationMac(prepared, apiKey, expiresAt);
  return timingSafeEqual(Buffer.from(match[2], 'hex'), Buffer.from(expected, 'hex'))
    ? null
    : 'confirmation_mismatch';
};
js
if (operation.confirmation.mode === 'hmac-sha256') {
  const token = createConfirmationToken(prepared, options.env ?? process.env, nowMs);
  preview = buildPreview(prepared, token);

  if (operation.confirmation.optIn === 'credential' && options.allowCredential !== true) {
    return { executed: false, reason: 'credential_opt_in_required', requiredFlag: '--allow-credential', preview };
  }
  if (operation.confirmation.optIn === 'destructive' && options.allowDestructive !== true) {
    return { executed: false, reason: 'destructive_opt_in_required', requiredFlag: '--allow-destructive', preview };
  }
  if (!options.confirm) return { executed: false, reason: 'confirmation_required', preview };
  const confirmationFailure = verifyConfirmationToken(options.confirm, prepared, apiKey, nowMs);
  if (confirm
...[truncated 2630 chars]
Remediation
View remediation

Remediation Suggestions

  1. Generate a cryptographically random nonce for every preview and include it in the HMAC-protected token.
  2. Maintain a trusted consumed-token store keyed by the token identifier or nonce.
  3. Perform an atomic check-and-consume operation immediately before attempting the network request. Reject tokens that have already been consumed.
  4. Mark the token consumed even if the request times out or produces an ambiguous transport failure; require a new preview and confirmation for another attempt.
  5. If durable local state is unsuitable, use a server-enforced idempotency key bound to the canonical request. The server must reject duplicate uses rather than merely deduplicating within the client process.
  6. Preserve the existing expiration, canonical-request binding, domain separation, and timing-safe HMAC comparison.
  7. Add automated tests proving that:
    • first use succeeds;
    • second use of the same token is rejected;
    • concurrent uses permit at most one request;
    • expired tokens are rejected;
    • changed request inputs invalidate confirmation;
    • ambiguous failures still consume the token.
  8. Until replay protection is implemented, avoid claiming that tokens are technically single-use and clearly document that single-use behavior relies on caller discipline.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (18)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Use `references/operations.json` for operation facts and `references/events.json` for event facts. Follow the localized public documentation URL stored on the

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Use `references/operations.json` for operation facts and `references/events.json` for event facts. Follow the localized public documentation URL stored on the

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Use `references/operations.json` for operation facts and `references/events.json` for event facts. Follow the localized public documentation URL stored on the

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Resolve the directory containing this `SKILL.md` and run `scripts/api-client.mjs` from that directory. Never replace it with raw `curl`, a generic HTTP tool, or

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Resolve the directory containing this `SKILL.md` and run `scripts/api-client.mjs` from that directory. Never replace it with raw `curl`, a generic HTTP tool, or

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Resolve the directory containing this `SKILL.md` and run `scripts/api-client.mjs` from that directory. Never replace it with raw `curl`, a generic HTTP tool, or

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Resolve the directory containing this `SKILL.md` and run `scripts/api-client.mjs` from that directory. Never replace it with raw `curl`, a generic HTTP tool, or

Missing User Warnings

High
Category
Not specified by scanner
Confidence
93% confidence
Finding

The account.auth.required event lists highly sensitive fields including auth_payload, code, password, qr, session, and token, yet the confirmation section states mode "none" and optIn null. This creates a clear documentation-level safety gap because credential-bearing events are described as available without any corresponding warning, disclosure, or consent indicator.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill declares access to an environment-backed credential (UNIFYPORT_API_KEY) and a runnable client, but it does not declare an explicit tool/permission scope limiting that capability. That creates a governance gap: platforms or reviewers cannot easily enforce least privilege, and a future change or prompt-path could invoke live operations with credential access under looser-than-intended controls.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

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

md
| `delete_webhook_endpoint_failed` | Webhook-endpoint deletion was rejected. |
| `update_workspace_failed` | Workspace update was rejected. |

Correct non-secret input and preview again. Never ask the user to paste a credential into chat.

## 401 Unauthorized

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/en/errors.md (reported line 33)May include surrounding context.

md
| `delete_webhook_endpoint_failed` | Webhook-endpoint deletion was rejected. |
| `update_workspace_failed` | Workspace update was rejected. |

Correct non-secret input and preview again. Never ask the user to paste a credential into chat.

## 401 Unauthorized

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/en/workflows.md (reported line 56)May include surrounding context.

md
1. Capture the HTTP status, public error code, and `request_id`.
2. Redact request/response fields before explaining the failure.
3. Check the public error reference and provider capability guide.
4. Ask for missing non-secret inputs; never ask the user to paste an API key or session.
5. Retry reads only when appropriate. Never blindly retry a write, destructive action, or credential operation.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This JSON manifest describes events that expose sensitive data such as message text, attachments, reply tokens, and account identifiers, while the corresponding confirmation block explicitly sets mode to "none" and optIn to null. Because SQP-2 applies to markdown and code only, this is only appropriate to flag if treated as a skill description/manifest-level disclosure gap: the file documents sensitive behavior without any user warning or consent mechanism for high-sensitivity event intake.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The language-selection rule states that Simplified Chinese is used only for Simplified Chinese conversations, and otherwise the skill explains in English. This forces a language choice for all other users without offering opt-in or alternatives, which matches the locale/language policy violation criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

Line L08 states that all other languages default to English, which imposes a language choice rather than offering the user a locale selection. The policy allows documented or opt-in language constraints, but this line does not present user choice or justify the fallback as region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The document presents only English and Simplified Chinese language choices in its navigation, which can be read as a locale constraint in the skill content. Because there is no explicit user choice mechanism or justification for limiting supported languages, this is a minor natural-language policy concern under the language/locale rule.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

This manifest enumerates language-specific documentation keys and explicitly fixes one locale variant as "zhCN" alongside English, but provides no natural-language indication that language selection is user-configurable or opt-in. Under the policy rule, forcing or preselecting locale behavior without documented user choice can be a natural-language locale-policy concern in config values.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

Line L09 instructs the workflow to use public documentation links that match the user's language, which imposes a language-handling policy in the skill text. The file does not indicate user opt-in, alternative language options, or a documented region-specific justification for this locale behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.