Back to skill

Security audit

EngageLab App Push

Security checks for vulnerabilities and agentic risk

Overview

This EngageLab push-notification skill is mostly purpose-aligned, but it handles powerful credentials and destructive remote actions without enough guardrails.

Install only if you are comfortable giving an agent access to EngageLab App Push operations. Use placeholders or environment variables instead of pasting Master Secrets into chat or shell history, restrict any helper code to official EngageLab HTTPS API hosts, and require explicit confirmation plus a prior lookup before broadcast sends, schedule changes, recalls, voice deletion, or user/device deletion.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/push_client.py:48
Finding
Basic authentication credentials can be transmitted to an arbitrary endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/push_client.py`, lines 48–68 **Vulnerability Type**: Unrestricted credential-bearing network destination **Risk Level**: High ### Vulnerable Code ```python def __init__(self, app_key: str, master_secret: str, base_url: str = BASE_URL): self._auth = (app_key, master_secret) self._headers = {"Content-Type": "application/json"} self._base_url = base_url.rstrip("/") def _request( self, method: str, path: str, payload: Optional[dict] = None, params: Optional[Any] = None, ) -> dict: url = f"{self._base_url}{path}" resp = requests.request( method, url, auth=self._auth, headers=self._headers, json=payload, params=params, ) ``` ### Technical Analysis The constructor accepts an unrestricted `base_url`, while `_request()` automatically attaches the AppKey and Master Secret through HTTP Basic authentication to every request. The code does not enforce HTTPS, validate the hostname, or restrict destinations to the four documented EngageLab API hosts. Supporting multiple EngageLab data centers does not require arbitrary destination support. A fixed mapping between data-center identifiers and approved HTTPS hosts would provide the declared functionality with less risk. Because Basic authentication transmits a reusable credential pair, an attacker-controlled endpoint can capture both credentials and API request data. Request payloads may also include notification contents, aliases, registration IDs, tags, schedules, or other operational information. ### Attack Path 1. An attacker influences application configuration, integration code, or user-provided parameters used to initialize the client. 2. The client is created with an attacker-controlled URL, such as: ```python client = EngageLabPush( app_key, master_secret, base_url="https://attacker.example" ) ``` 3. The application invokes any ...[truncated 1045 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace unrestricted `base_url` input with a data-center enumeration mapped to fixed hosts: - `pushapi-sgp.engagelab.com` - `pushapi-usva.engagelab.com` - `pushapi-defra.engagelab.com` - `pushapi-hk.engagelab.com` 2. Require the `https` scheme and reject embedded credentials, unexpected ports, IP literals, redirects to other hosts, and malformed hostnames. 3. If custom endpoints are needed for testing, require an explicit unsafe-development option and do not attach production credentials by default. 4. Normalize and validate the final hostname before each credential-bearing request. 5. Disable automatic cross-host authentication forwarding and validate redirect destinations, or disable redirects entirely. 6. Store credentials in a protected secret manager or environment variables rather than source code or user-controlled configuration. 7. Rotate any credentials that may already have been transmitted to an untrusted endpoint. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:66
Finding
Documentation encourages disclosure of the Master Secret through chat and command-line arguments<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 66–75 and 225–233 **Vulnerability Type**: Unsafe secret acquisition and command-line credential handling **Risk Level**: Medium ### Vulnerable Code ```markdown **Example** (curl): ```bash curl -X POST https://pushapi-sgp.engagelab.com/v4/push \ -H "Content-Type: application/json" \ -u "YOUR_APP_KEY:YOUR_MASTER_SECRET" \ -d '{ "from": "push", "to": "all", "body": { "platform": "all", "notification": { "alert": "Hello!" } } }' ``` If the user hasn't provided credentials, ask for **AppKey** and **Master Secret** before generating API calls. ``` The same file later states: ```markdown When the user asks to send push or use other App Push APIs, generate working code. Default to **curl** unless the user specifies a language. Supported patterns: - **curl** — Shell with `-u "AppKey:MasterSecret"` and correct base URL - **Python** — `requests` with Basic auth - **Node.js** — `fetch` or `axios` - **Java** — `HttpClient` - **Go** — `net/http` Always include the Authorization header and error handling. Use placeholders like `YOUR_APP_KEY` and `YOUR_MASTER_SECRET` if credentials are not provided. ``` ### Technical Analysis The Skill explicitly instructs the Agent to ask users for an AppKey and Master Secret. Supplying a live Master Secret through an Agent conversation is unnecessary for generating integration code and may expose the secret to conversation retention, telemetry, support access, audit logs, or other components in the Agent execution environment. The recommended curl form places the credential pair directly in a command-line argument through `-u`. Depending on the operating system and shell environment, such values may be retained in shell history, copied into terminal logs, exposed in automation logs, or temporarily visible through process inspection. Although the examples use placeholders, the instruction to ask for real credentials creates a foreseeable path in which pla ...[truncated 1287 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the instruction to ask users for live AppKeys or Master Secrets in conversation. 2. Always generate examples with placeholders that are resolved locally at execution time. 3. Prefer protected environment variables or a secret manager: ```bash read -rsp "Master Secret: " ENGAGELAB_MASTER_SECRET export ENGAGELAB_MASTER_SECRET ``` 4. For automation, use a permission-restricted configuration file or secret-injection mechanism rather than embedding secrets in command arguments. 5. Warn users not to paste secrets into chat, source code, issue trackers, terminal transcripts, or logs. 6. Redact authorization headers and credentials from error reporting and diagnostic output. 7. Recommend narrowly scoped credentials where the provider supports them, along with periodic rotation and immediate revocation after suspected disclosure. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/callback-api.md:29
Finding
Callback authentication is optional and lacks replay protection guidance<![CDATA[ ## Vulnerability Details **File Location**: `references/callback-api.md`, lines 29–50 **Vulnerability Type**: Insufficient webhook authentication and replay protection **Risk Level**: Medium ### Vulnerable Code ```markdown ## Security (optional) To verify that the callback is from EngageLab, configure a **callback username** and **callback secret** in the console. Then verify the `X-CALLBACK-ID` header. **Header example:** ``` X-CALLBACK-ID: timestamp=1681991058;nonce=123123123123;username=test;signature=59682d71e2aa2747252e4e62c15f6f241ddecc8ff08999eda7e0c4451207a16b ``` - `timestamp` — Callback message timestamp. - `nonce` — Random number. - `username` — Callback username you configured. - `signature` — Signature to verify. **Signature algorithm:** ``` signature = HMAC-SHA256(secret, timestamp + nonce + username) ``` Use your configured **callback secret** as the HMAC key, and the string `timestamp + nonce + username` (concatenated) as the message. Compare the computed signature with the `signature` in the header. ``` The summary also states: ```markdown 3. Optionally verify `X-CALLBACK-ID` with HMAC-SHA256(secret, timestamp+nonce+username). ``` ### Technical Analysis The documentation presents callback signature verification as optional even though callback data can feed analytics or event-driven business logic. An implementation that follows this guidance may accept arbitrary POST requests as genuine EngageLab events. The guidance also omits several controls required for robust webhook authentication: - Validation that `username` equals the configured callback username. - A strict timestamp freshness window. - Nonce tracking to reject replayed callbacks. - Constant-time comparison of the received and computed HMAC values. - Strict parsing and validation of all `X-CALLBACK-ID` fields. - Clear separation between initial `echostr` endpoint validation and authenticated event processing. The documented signature covers timestamp, nonce, ...[truncated 1814 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make HMAC authentication mandatory for every callback event, except any narrowly defined initial validation flow required by the provider. 2. Parse `X-CALLBACK-ID` strictly and reject missing, duplicate, malformed, or unexpected fields. 3. Verify that the supplied username exactly matches the configured callback username. 4. Compute the expected HMAC with the protected callback secret and compare it using a constant-time function such as `hmac.compare_digest`. 5. Enforce a short timestamp freshness window, accounting for limited clock skew. 6. Cache accepted nonces for at least the freshness window and reject reuse. 7. Require HTTPS for the callback URL. 8. Ensure callback processing is idempotent by storing a stable event identifier or an appropriate composite key. 9. Return an authentication error for invalid callbacks and avoid processing their payloads. 10. Keep the callback secret in a secret manager and support rotation. 11. Clearly document whether and how the callback body is authenticated under the provider protocol. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/push_client.py:61
Finding
HTTP requests can block indefinitely because no timeout is configured<![CDATA[ ## Vulnerability Details **File Location**: `scripts/push_client.py`, lines 61–68 **Vulnerability Type**: Missing network timeout **Risk Level**: Low ### Vulnerable Code ```python resp = requests.request( method, url, auth=self._auth, headers=self._headers, json=payload, params=params, ) ``` ### Technical Analysis The `requests.request()` call does not define a connection or read timeout. Python Requests waits indefinitely by default unless the underlying operation fails for another reason. A slow, unavailable, or malicious endpoint can therefore retain a connection without returning a complete response. This is especially relevant because the client also permits a custom `base_url`, but ordinary upstream failures can trigger the issue even when the legitimate EngageLab endpoint is used. In synchronous services, each stalled request can occupy a worker thread or process. Repeated requests may eventually exhaust the available worker pool and prevent unrelated operations from completing. ### Attack Path 1. The application invokes an API method through `EngageLabPush`. 2. The destination accepts the connection but delays or withholds the response. 3. Because no timeout is set, the calling thread remains blocked indefinitely. 4. Additional requests create more blocked workers. 5. The application exhausts its worker pool, connection capacity, or other resources and becomes unavailable. An attacker could make this more reliable by controlling the custom `base_url`, but control of the legitimate upstream is not required; a network fault can produce similar effects. ### Impact Assessment The primary impact is availability degradation: - Application threads or workers can remain blocked. - Request queues may grow until the service stops responding. - Scheduled notification and management operations may be delayed. - Resource exhaustion can affect unrelated application functions sharing the same worker pool. This issue does n ...[truncated 68 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Configure explicit connection and read timeouts: ```python resp = requests.request( method, url, auth=self._auth, headers=self._headers, json=payload, params=params, timeout=(5, 30), ) ``` 2. Allow timeout values to be configured within safe minimum and maximum bounds. 3. Handle `requests.Timeout` separately and return a controlled application error. 4. Apply retries only to operations that are demonstrably idempotent. 5. Do not automatically retry push creation, deletion, or other state-changing operations without an idempotency mechanism. 6. Add circuit breaking and bounded concurrency in long-running services. ]]>
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 (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
The code is broadly aligned with the declared EngageLab App Push purpose and does not show unrelated or suspicious behavior. However, the description materially overstates several capabilities that are not present in this code chunk: there are no methods for configuring callbacks, uploading OPPO images, or push-to-speech. The code also does not expose any explicit in-app-message-specific endpoint, though generic payload passthrough may partially cover some message use cases. Because the declared description claims these concrete capabilities but the supplied code does not implement them, this is a description/behavior mismatch.

Missing User Warnings

High
Confidence
95% confidence
Finding
The skill advertises irreversible user-deletion capability without built-in warning or confirmation guidance. In a tool-using agent, that omission makes destructive actions easier to trigger accidentally or through ambiguous user prompts, potentially deleting device records and associated metadata without recovery.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- Create: `POST /v4/schedules` — Body includes `name`, `enabled`, `trigger` (single / periodical / intelligent), `push` (same as Create Push body).
- Get: `GET /v4/schedules/{schedule_id}`.
- Update: `PUT /v4/schedules/{schedule_id}`.
- Delete: `DELETE /v4/schedules/{schedule_id}`.

Trigger types: **single** (one-time, `time`, `zone_type`), **periodical** (start, end, time, time_unit, point, zone_type), **intelligent** (backup_time: `"now"` or `"yyyy-MM-dd HH:mm:ss"`). See `doc/apppush/REST API/Scheduled Tasks API.md`.
Confidence
90% confidence
Finding
The skill exposes a destructive DELETE operation for scheduled tasks without any documented validation or confirmation guardrails. If an agent fills the schedule_id from ambiguous context or prompt injection, it could cancel active campaigns or business-critical notifications.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
## Message Recall

`DELETE /v4/push/withdraw/{msg_id}` — Recall within one day; no duplicate recall.

## Delete User
Confidence
88% confidence
Finding
Message recall is a state-changing operation that can withdraw previously sent notifications, yet the skill provides no safety pattern for validating msg_id ownership, recency, or user intent. In an automated setting, this can be abused to disrupt communications or revoke messages unintentionally.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
## Delete User

`DELETE /v4/devices/{registration_id}` — Asynchronously deletes user and all related data (tags, alias, device info, timezone). Cannot be restored. Batch deletion not supported.

## Statistics
Confidence
95% confidence
Finding
Deleting a device record by registration_id is irreversible and removes associated tags, aliases, and device metadata, but the skill presents it as a routine operation without strong safeguards. This creates a direct path for accidental or malicious destruction of user records if parameters are misbound or socially engineered.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- Create/update: `POST /v4/voices` — `Content-Type: multipart/form-data`; fields: `language` (en, zh-Hans, zh-Hant), `file` (zip of mp3 files). Returns `file_url`.
- List: `GET /v4/voices`.
- Delete: `DELETE /v4/voices?language=en`.

Use `options.voice_value` in push body to reference voice for TTS. See `doc/apppush/REST API/Push-to-Speech API.md`.
Confidence
80% confidence
Finding
The documented voice-delete endpoint removes TTS assets by language parameter without any mention of preview or confirmation. While less severe than user deletion, it can still break downstream push-to-speech behavior or remove shared assets needed by future campaigns.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
return self._request("POST", f"/v4/devices/{registration_id}", payload)

    def delete_device(self, registration_id: str) -> dict:
        """Delete user and all related data (DELETE /v4/devices/{registration_id}). Async."""
        return self._request("DELETE", f"/v4/devices/{registration_id}")

    # ── Tag count ─────────────────────────────────────────────────────
Confidence
90% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# ── Message recall ────────────────────────────────────────────────

    def recall(self, msg_id: str) -> dict:
        """Recall a message (DELETE /v4/push/withdraw/{msg_id}). Only within one day."""
        return self._request("DELETE", f"/v4/push/withdraw/{msg_id}")

    # ── Test push (validate) ──────────────────────────────────────────
Confidence
90% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
return self._request("PUT", f"/v4/schedules/{schedule_id}", payload)

    def schedule_delete(self, schedule_id: str) -> dict:
        """Delete scheduled task (DELETE /v4/schedules/{schedule_id})."""
        return self._request("DELETE", f"/v4/schedules/{schedule_id}")

    # ── Statistics ────────────────────────────────────────────────────
Confidence
90% 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
89% confidence
Finding
The skill documents and encourages outbound network operations to EngageLab APIs, but it does not declare any explicit tool scope or permissions boundary. In agent environments, missing scope declarations can let a skill invoke network-capable helpers without transparent review, increasing the chance of unintended data transmission or unauthorized API use.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger phrases are broad enough to activate on many generic 'push notification' or messaging-related requests, which can cause the skill to be selected outside its intended context. In an agentic system, over-broad activation increases the risk of sending users into a networked, side-effect-capable skill when they only wanted general advice or a different provider.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The skill includes message recall and scheduled-task deletion/update operations without cautionary UX guidance. These actions can disrupt planned campaigns or retract live messaging, so omission of confirmation and validation steps increases the risk of accidental operational harm.

External Transmission

Medium
Category
Data Exfiltration
Content
**Example** (curl):

```bash
curl -X POST https://pushapi-sgp.engagelab.com/v4/push \
  -H "Content-Type: application/json" \
  -u "YOUR_APP_KEY:YOUR_MASTER_SECRET" \
  -d '{ "from": "push", "to": "all", "body": { "platform": "all", "notification": { "alert": "Hello!" } } }'
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
89% confidence
Finding
The document states that message status callbacks are sent to the business server for analytics and event handling, which implies network transmission of potentially sensitive delivery or click-event data. The page explains how to receive and validate callbacks but does not include any user-facing warning about privacy, data handling, or exposure implications of enabling this integration.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
|------|-------------|-------------|
| 200 | OK | Success. |
| 400 | Bad request | The request is invalid. The response body will explain the reason. |
| 401 | Not verified | No verification information or verification failed. |
| 403 | Rejected | The request was understood but not accepted. The response will explain. |
| 404 | Not found | The resource does not exist, or the requested format is not supported. |
| 405 | Method not allowed | The interface does not support the request method. |
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

Medium
Confidence
84% confidence
Finding
This code sends AppKey and Master Secret via authenticated HTTP requests to an external push API, which is a network operation involving sensitive credentials. While the module docstring describes Basic Authentication generally, the request path itself has no user-facing warning, confirmation, or logging when transmitting credentials or push payload data.

Static analysis

No suspicious patterns detected.