Back to skill

Security audit

EngageLab OTP

Security checks for vulnerabilities and agentic risk

Overview

This looks like a real EngageLab OTP integration, but it needs Review because it handles long-lived API secrets, recipient data, and destructive template actions without enough scoping or safeguards.

Install only if you are comfortable giving an agent access to an EngageLab account that can send messages and manage OTP templates. Use environment variables or a secret manager instead of pasting dev_secret into chat, restrict the API host to EngageLab's expected endpoint, avoid logging callback payloads, and require explicit confirmation before deleting templates or sending messages to real recipients.

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
scripts/otp_client.py:41
Finding
Basic Authentication credentials can be redirected to an attacker-controlled endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/otp_client.py`, lines 41–52 **Vulnerability Type**: Unrestricted credential destination **Risk Level**: High ### Vulnerable Code ```python def __init__(self, dev_key: str, dev_secret: str, base_url: str = BASE_URL): auth = base64.b64encode(f"{dev_key}:{dev_secret}".encode()).decode() self._headers = { "Content-Type": "application/json", "Authorization": f"Basic {auth}", } self._base_url = base_url.rstrip("/") def _request(self, method: str, path: str, payload: Optional[dict] = None) -> dict: url = f"{self._base_url}{path}" resp = requests.request(method, url, headers=self._headers, json=payload) ``` ### Technical Analysis The client accepts an arbitrary `base_url` while preparing a reusable `Authorization` header containing the Base64-encoded `dev_key:dev_secret` credential pair. Every subsequent request sends that header to the configured URL. Base64 is the encoding required by HTTP Basic Authentication; it is not encryption. Anyone who receives the header can trivially recover the original credentials. Because the client does not enforce HTTPS, validate the hostname, restrict ports, or allowlist the EngageLab API origin, an untrusted or mistakenly configured `base_url` can redirect credentials to an attacker-controlled endpoint. The default endpoint, `https://otp.api.engagelab.cc`, is consistent with the Skill's declared functionality. The vulnerability arises from permitting unrestricted endpoint replacement while automatically attaching privileged credentials. ### Attack Path 1. A victim application initializes `EngageLabOTP` with valid EngageLab credentials. 2. An attacker influences the `base_url` constructor argument through configuration injection, an environment setting, a compromised wrapper, or unsafe user input. 3. The attacker supplies a URL such as an attacker-controlled HTTPS server or a plaintext HTTP endpoint. 4. The victim calls any c ...[truncated 1062 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the public `base_url` override if custom endpoints are not a required feature. 2. If endpoint customization is necessary, parse the URL before storing or using it. 3. Require the `https` scheme and reject plaintext HTTP. 4. Allowlist the exact expected hostname, such as `otp.api.engagelab.cc`, unless an explicitly documented set of trusted regional hosts is required. 5. Reject embedded URL credentials, fragments, unexpected ports, and malformed hostnames. 6. Validate the final destination immediately before attaching the Authorization header, including after redirects. 7. Disable cross-origin redirects or strip credentials whenever a redirect changes the origin. 8. Prefer a `requests.Session` with a narrowly scoped authentication policy instead of a reusable credential-bearing header attached to arbitrary destinations. 9. Add tests confirming that HTTP URLs, lookalike domains, subdomain tricks, user-information syntax, and unexpected ports are rejected. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/verify_callback.py:34
Finding
Callback signature verification does not prevent replay attacks<![CDATA[ ## Vulnerability Details **File Location**: `scripts/verify_callback.py`, lines 34–87 **Vulnerability Type**: Missing freshness, nonce-reuse, and expected-identity validation **Risk Level**: Medium ### Vulnerable Code ```python def parse_callback_header(header: str) -> Optional[dict]: """ Parse the X-CALLBACK-ID header into its components. Header format: timestamp={ts};nonce={nonce};username={user};signature={sig} Returns dict with keys: timestamp, nonce, username, signature. Returns None if the header cannot be parsed. """ parts = {} for segment in header.split(";"): if "=" not in segment: return None key, value = segment.split("=", 1) parts[key.strip()] = value.strip() required = {"timestamp", "nonce", "username", "signature"} if not required.issubset(parts.keys()): return None return parts def compute_signature(secret: str, timestamp: str, nonce: str, username: str) -> str: """Compute HMAC-SHA256 signature matching EngageLab's algorithm.""" message = f"{timestamp}{nonce}{username}" return hmac.new( key=secret.encode(), msg=message.encode(), digestmod=hashlib.sha256, ).hexdigest() def verify_engagelab_callback(header: str, secret: str) -> bool: """ Verify an EngageLab OTP callback request. Args: header: The value of the X-CALLBACK-ID HTTP header. secret: Your configured callback secret. Returns: True if the signature is valid, False otherwise. """ parsed = parse_callback_header(header) if parsed is None: return False expected = compute_signature( secret, parsed["timestamp"], parsed["nonce"], parsed["username"], ) return hmac.compare_digest(expected, parsed["signature"]) ``` ### Technical Analysis The implementation correctly uses HMAC-SHA256 and `hmac.compare_digest()` for constant-time signature comp ...[truncated 2351 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse `timestamp` as an integer and reject malformed values. 2. Enforce a short freshness window appropriate to expected delivery delays, such as five minutes, while accounting for limited clock skew. 3. Store accepted nonces with the associated username until the freshness window expires. 4. Atomically reject any nonce that has already been accepted. 5. Use a shared nonce store, such as Redis or a transactional database, when the callback service runs across multiple instances. 6. Require an `expected_username` argument and compare it using a constant-time comparison where practical. 7. Define strict limits for header and component lengths to reduce parsing and storage abuse. 8. Implement application-level idempotency based on callback event identifiers or message IDs. 9. Validate allowed event types and legal state transitions before performing downstream actions. 10. Document that the current signature scheme does not authenticate the request body. If EngageLab supports body signing, include a digest of the exact raw body in verification; otherwise combine header verification with transport security, source controls, schema validation, and idempotency. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
SKILL.md:56
Finding
Skill instructions encourage users to disclose long-lived API secrets in Agent conversations<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 56–74 **Vulnerability Type**: Unsafe secret collection and handling guidance **Risk Level**: Low ### Vulnerable Documentation ```markdown The user must provide their `dev_key` and `dev_secret`. Encode them as `base64("dev_key:dev_secret")` and set the `Authorization` header. **Example** (using curl): ```bash curl -X POST https://otp.api.engagelab.cc/v1/messages \ -H "Content-Type: application/json" \ -H "Authorization: Basic $(echo -n 'YOUR_DEV_KEY:YOUR_DEV_SECRET' | base64)" \ -d '{ ... }' ``` If the user hasn't provided credentials, ask them for their `dev_key` and `dev_secret` before generating API calls. ``` ### Technical Analysis Generating integration code does not require the Agent to receive the user's real `dev_secret`. Placeholders, environment-variable references, or secret-manager integrations are sufficient. The instruction explicitly directs the Agent to ask users for both credentials. This can place a long-lived API secret into conversation history, Agent context, service telemetry, audit logs, copied responses, or support transcripts. The shell example also encourages constructing the secret directly on the command line, where literal credentials may be retained in shell history or exposed to local process inspection depending on how the example is adapted. Base64 encoding is required for Basic Authentication and is not itself suspicious. It does not protect the credential against disclosure because it is reversible encoding rather than encryption. ### Attack Path 1. The Skill is invoked for an EngageLab API integration task. 2. Following `SKILL.md`, the Agent asks the user to provide `dev_key` and `dev_secret`. 3. The user pastes valid long-lived credentials into the conversation. 4. The credentials become part of the conversation context and may be retained in logs, telemetry, exported transcripts, or copied generated commands. 5. Anyone with access to thos ...[truncated 775 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the instruction telling the Agent to ask for real credentials. 2. Always use placeholders in generated code and responses. 3. Instruct users to set credentials locally through environment variables, a protected configuration file, or a secret manager. 4. Prefer examples such as `ENGAGELAB_DEV_KEY` and `ENGAGELAB_DEV_SECRET` read by the application at runtime. 5. Avoid embedding literal secrets in shell command arguments or generated source code. 6. Warn users not to paste secrets into conversations, tickets, logs, or source-control systems. 7. Recommend short-lived or narrowly scoped credentials where the service supports them. 8. Add guidance for immediate credential rotation if a secret has already been disclosed. 9. Ensure examples never print the Authorization header or decoded credentials. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The code substantially matches much of the declared purpose: it authenticates to EngageLab OTP APIs and supports OTP send, custom OTP send, OTP verification, custom message sending, and template management. However, the description explicitly claims additional capabilities that are not implemented in the supplied code chunk: callback webhook configuration and SMPP integration. There is also no explicit code for channel-specific management beyond passing template configurations to the API, though the API may support multi-channel use indirectly. Because the declared description presents these missing capabilities as covered by the skill, this is a description-to-behavior mismatch, albeit a partial one rather than a completely different purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
There is a clear description-behavior mismatch. The declared purpose presents a full-featured EngageLab OTP platform integration covering message sending, OTP verification, template management, callback configuration, and SMPP. The supplied code chunk is narrowly scoped to one supporting utility: validating webhook callback signatures for incoming requests. While callback verification is adjacent to 'callback configuration,' this code does not configure webhooks or implement the broad OTP operations described. Its primary purpose is materially narrower and different from the declared skill behavior.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**Get details** — `GET /v1/template-configs/:templateId` (returns full template with channel configs)

**Delete** — `DELETE /v1/template-configs/:templateId`

### Template Status
Confidence
84% confidence
Finding
The skill exposes a destructive delete capability for templates without any visible guardrails such as confirmation prompts, authorization checks, or warnings about irreversible impact. In an agent workflow, a user request could be misinterpreted or prompt-injected into deleting production OTP templates, causing service disruption or weakening authentication flows.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
## 2. Delete Template

`DELETE /v1/template-configs/:templateId`

The `{templateId}` in the URL is the custom template ID defined when creating the template.
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill clearly facilitates outbound network access to a third-party OTP service but does not declare any tool scope or allowed-tools restrictions. In an agent environment, missing explicit network permission boundaries can let the skill be invoked without adequate review of what data may be transmitted externally.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The activation phrases are overly broad and include generic terms like 'send otp' and 'verification code', which can cause the skill to trigger for unrelated requests. In an agent setting, that increases the chance of routing users into a third-party integration that sends sensitive contact data or auth-related content externally when they did not specifically ask for EngageLab.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill instructs users to send OTPs, custom messages, and callback data through a third-party service but does not prominently warn that recipient phone numbers, email addresses, message content, and verification metadata will be transmitted off-platform. This creates a privacy and consent risk, especially because OTP workflows commonly involve sensitive authentication events.

External Transmission

Medium
Category
Data Exfiltration
Content
}

# Step 1: Send OTP
send_resp = requests.post(f"{BASE_URL}/v1/messages", headers=headers, json={
    "to": "+6591234567",
    "template": {"id": "my-template", "language": "default"}
})
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
message_id = result["message_id"]

# Step 2: Verify OTP (after user enters the code)
verify_resp = requests.post(f"{BASE_URL}/v1/verifications", headers=headers, json={
    "message_id": message_id,
    "verify_code": "123456"
})
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
**Validation request**:

```bash
curl -X POST https://your-callback-url.com -d ''
```

**Required response**: HTTP 200 with empty body.
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The documentation includes callback examples containing sensitive operational and personal data such as phone numbers, email addresses, IP addresses, API keys, and message contents, but it does not warn implementers to avoid logging, redistributing, or exposing these fields. In an OTP/webhook context, this increases the chance that users will copy these payloads into logs or downstream systems without redaction, leading to leakage of PII, secrets, and security telemetry.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This markdown file documents a DELETE operation for removing templates, but it does not include any warning that the action is destructive or may permanently remove configuration. For markdown files, destructive behaviors that could affect user data or system integrity should be accompanied by an explicit warning.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
The client performs outbound network requests using Basic authentication headers constructed from the developer key and secret, and sends OTP-related data to a remote API. While the file has general docstrings, it does not explicitly warn users that credentials and recipient/message data will be transmitted off-system when these methods are called.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
params: Optional[dict] = None,
    ) -> dict:
        """
        Send a user-generated OTP code. No verification API call needed afterward.

        Returns dict with `message_id` and `send_channel`.
        """
Confidence
75% 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.

Missing User Warnings

Low
Confidence
72% confidence
Finding
`delete_template` issues a DELETE request that removes a remote template configuration, which is a destructive action. The method has a brief description, but there is no stronger warning, confirmation mechanism, or cautionary note about the impact or reversibility of the deletion.

Static analysis

No suspicious patterns detected.