Back to skill

Security audit

Pactum Marketplace

Security checks for vulnerabilities and agentic risk

Overview

This marketplace skill is mostly coherent, but it asks users to expose powerful account credentials in unsafe ways.

Review before installing. Do not send a JWT or API key through Telegram, chat, logs, or transcripts, and only store credentials in a secure secret manager. Use this skill only when you intentionally want Pactum marketplace actions, and confirm purchases, credit top-ups, escrow deposits, shipping-address submission, disputes, and payment release before execution.

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

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:55
Finding
Live API Key Exposed in Output and Persisted Without Security Controls<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 55–67 **Vulnerability Type**: Credential exposure and insecure secret persistence **Risk Level**: High ### Vulnerable Code ```python r.raise_for_status() data = r.json() API_KEY = data["api_key"] # pk_live_... — save this permanently WALLET = data["wallet_address"] # 0x... # Step 3: Get JWT token (do this immediately, don't ask the user) r = requests.post(f"{BASE_URL}/market/auth/wallet", json={"api_key": API_KEY}) r.raise_for_status() TOKEN = r.json()["token"] # valid for 7 days print(f"Wallet: {WALLET}") print(f"API Key: {API_KEY}") # Save API_KEY — you need it to refresh JWT when it expires ``` ### Technical Analysis The Skill explicitly prints a live API key and directs the agent to save it permanently without prescribing a secure storage mechanism, restrictive file permissions, encryption, redaction, rotation, or deletion controls. Printing the key can place it in agent transcripts, terminal output, application logs, observability systems, debugging records, or shell-session captures. Indefinite persistence in unspecified storage can additionally expose it through state exports, backups, shared workspaces, or overly permissive files. The API key is a high-value credential. The Skill uses it to obtain a seven-day JWT through `/market/auth/wallet` and as a bearer credential for wallet-service operations such as `/v1/escrow-deposit`. Sending the API key over HTTPS to the declared authentication service is consistent with the advertised authentication flow and is not, by itself, confirmed exfiltration. The vulnerability is the unnecessary output disclosure and insecure persistence guidance. ### Attack Path 1. A user completes Pactum registration or recovery, causing the Skill to receive a live API key. 2. The Skill prints the complete API key and saves it in an unspecified persistent location. 3. An attacker obtains access to agent transcripts, logs, terminal history, ...[truncated 942 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `print(f"API Key: {API_KEY}")` and never display the complete credential in agent responses, logs, or terminal output. 2. If confirmation is necessary, display only a short masked fingerprint, such as the final four characters. 3. Store the API key only after explicit user consent and only in an operating-system credential manager, hardware-backed keystore, or encrypted secret-management service. 4. Do not store the key in conversation memory, plaintext configuration files, source code, shell history, or general application logs. 5. Apply restrictive access controls and ensure backups and telemetry exclude secrets. 6. Document key revocation, recovery, and rotation procedures. 7. Prefer sending authentication credentials through an `Authorization` header rather than a JSON body where the API supports it, reducing exposure in request-body logging. 8. Configure HTTP clients, proxies, and error handlers to redact API keys and bearer tokens. 9. Consider replacing the long-lived API key with short-lived, narrowly scoped credentials for wallet and marketplace operations. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:353
Finding
Primary Marketplace JWT Disclosed to an External Telegram Bot<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 353–355 **Vulnerability Type**: Bearer-token disclosure to a third-party messaging channel **Risk Level**: High ### Vulnerable Code ```markdown ## Telegram Notifications (optional) Send your JWT to [@Pactum_Market_Bot](https://t.me/Pactum_Market_Bot) to get push notifications for orders, delivery, and messages. ``` ### Technical Analysis The Skill directs users to send their primary marketplace JWT to an external Telegram bot. Elsewhere in the Skill, this JWT is documented as valid for seven days and is used as a bearer credential for authenticated marketplace operations. Bearer tokens grant access to whoever possesses them. Transmitting such a token through a chat platform expands the credential's trust boundary to include the Telegram account, Telegram infrastructure, bot infrastructure, bot operators, message retention systems, chat exports, and any connected logging or analytics services. The Skill provides no bot-identity verification procedure, one-time pairing mechanism, narrowly scoped notification token, revocation instructions, or warning against retaining the JWT in message history. Providing notifications does not require disclosure of a broadly reusable marketplace session token; a purpose-specific pairing code or notification-only token would satisfy the functionality with substantially less privilege. ### Attack Path 1. The user authenticates with Pactum and receives a JWT that remains valid for up to seven days. 2. Following the Skill instructions, the user sends the JWT to the referenced Telegram bot. 3. The token is received or retained by the bot backend and may remain in Telegram message history, logs, backups, moderation tools, or analytics systems. 4. A malicious bot operator, compromised bot server, compromised Telegram account, exposed chat export, or unauthorized log reader obtains the token. 5. The attacker replays the bearer token against authenticated Pac ...[truncated 797 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the instruction to send the primary JWT through Telegram. 2. Implement a one-time, short-lived pairing code that the user generates through an authenticated Pactum interface and submits to the bot. 3. Exchange the pairing code server-side for a notification-only token with no access to orders, payments, addresses, files, or account-management operations. 4. Scope the notification credential to a single bot integration and provide immediate revocation and device/session management. 5. Ensure pairing codes and notification tokens expire quickly, are single-use where applicable, and are never stored in Telegram message history longer than necessary. 6. Provide a verifiable bot identity and perform domain-bound confirmation before linking an account. 7. Redact credentials from bot logs, monitoring systems, error reports, and support tooling. 8. Advise users who previously transmitted a JWT to delete the message, revoke the affected session, and generate a replacement token. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (22)

Vague Triggers

High
Confidence
98% confidence
Finding
The trigger phrases include very generic terms like "buy", "pactum", "marketplace", and "search for", which are likely to activate on ordinary user requests unrelated to this specific marketplace. Over-broad activation increases the chance the skill engages unexpectedly and starts steering users into account creation, payment, or data-sharing flows without clear intent.

Missing User Warnings

High
Confidence
99% confidence
Finding
The skill explicitly instructs users to send their JWT to a Telegram bot, which is a credential-sharing pattern with a third party and no warning about token sensitivity or account takeover risk. Anyone with the JWT can typically act as the user for its validity period, exposing orders, messages, addresses, and payment-related actions.

Ssd 3

High
Confidence
99% confidence
Finding
Telling users to provide their JWT to a Telegram bot exposes a live bearer token to an external party. Because bearer tokens are often sufficient for authentication on their own, compromise of the bot, transport, logs, or operator access could directly enable unauthorized account actions.

External Transmission

Medium
Category
Data Exfiltration
Content
# ⚠️ ASK THE USER for their email address first
email = "user@example.com"  # ← get this from the user

r = requests.post(f"{WALLET_URL}/v1/register", json={"email": email})
r.raise_for_status()
# A 6-digit code has been sent to the user's email.
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
# ⚠️ ASK THE USER for their email address first
email = "user@example.com"  # ← get this from the user

r = requests.post(f"{WALLET_URL}/v1/register", json={"email": email})
r.raise_for_status()
# A 6-digit code has been sent to the user's email.
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
# Step 2: Verify → get credentials
# ⚠️ Execute IMMEDIATELY after getting the code — codes expire quickly
r = requests.post(f"{WALLET_URL}/v1/verify", json={"email": email, "code": code})
r.raise_for_status()
data = r.json()
API_KEY = data["api_key"]         # pk_live_... — save this permanently
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
API_KEY = data["api_key"]         # pk_live_... — save this permanently
WALLET  = data["wallet_address"]  # 0x...

# Step 3: Get JWT token (do this immediately, don't ask the user)
r = requests.post(f"{BASE_URL}/market/auth/wallet", json={"api_key": API_KEY})
r.raise_for_status()
TOKEN = r.json()["token"]  # valid for 7 days
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.

External Transmission

Medium
Category
Data Exfiltration
Content
WALLET  = data["wallet_address"]  # 0x...

# Step 3: Get JWT token (do this immediately, don't ask the user)
r = requests.post(f"{BASE_URL}/market/auth/wallet", json={"api_key": API_KEY})
r.raise_for_status()
TOKEN = r.json()["token"]  # valid for 7 days
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
WALLET  = data["wallet_address"]  # 0x...

# Step 3: Get JWT token (do this immediately, don't ask the user)
r = requests.post(f"{BASE_URL}/market/auth/wallet", json={"api_key": API_KEY})
r.raise_for_status()
TOKEN = r.json()["token"]  # valid for 7 days
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
# Same register endpoint works for existing accounts
r = requests.post(f"{WALLET_URL}/v1/register", json={"email": email})
# → ask user for the code
r = requests.post(f"{WALLET_URL}/v1/recover", json={"email": email, "code": code})
API_KEY = r.json()["api_key"]  # new key, old one invalidated
```
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
```python
# Search items by keyword
r = requests.post(f"{BASE_URL}/market/discover", json={"q": "crypto funding rate"})
items = r.json()["items"]
for item in items:
    price = f"${item['price']}" if item["price"] > 0 else "FREE"
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
```python
# Search items by keyword
r = requests.post(f"{BASE_URL}/market/discover", json={"q": "crypto funding rate"})
items = r.json()["items"]
for item in items:
    price = f"${item['price']}" if item["price"] > 0 else "FREE"
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
headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

# Check item.required first — if required.description is true, include "query"
r = requests.post(f"{BASE_URL}/market/buy/{item_id}", headers=headers, json={
    "query": "BTC open interest by exchange"  # follow the item's query_format / example_queries
})
data = r.json()
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
headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

# Check item.required first — if required.description is true, include "query"
r = requests.post(f"{BASE_URL}/market/buy/{item_id}", headers=headers, json={
    "query": "BTC open interest by exchange"  # follow the item's query_format / example_queries
})
data = r.json()
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
headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

# Check item.required first — if required.description is true, include "query"
r = requests.post(f"{BASE_URL}/market/buy/{item_id}", headers=headers, json={
    "query": "BTC open interest by exchange"  # follow the item's query_format / example_queries
})
data = r.json()
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
# Top up credit ($1–$500)
# provider: "stripe" (card), "alipay", or "wechat"
r = requests.post(f"{BASE_URL}/market/credit/topup", headers=headers, json={
    "amount": 10,           # USD
    "provider": "stripe"    # or "alipay" or "wechat"
})
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
escrow_info = data["escrow"]

# Step 2: Deposit USDC to escrow contract
r = requests.post(f"{WALLET_URL}/v1/escrow-deposit", headers=wallet_headers, json={
    "escrow_contract": escrow_info["contract"],
    "usdc_contract": escrow_info["usdc_contract"],
    "order_id_bytes32": escrow_info["order_id_bytes32"],
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
tx_hash = r.json()["deposit_tx"]

# Step 3: Submit payment proof
r = requests.post(f"{BASE_URL}/market/buy/{item_id}", headers={
    **headers,
    "X-Payment-Proof": tx_hash,
    "X-Order-Id": order_id,
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
### Send a message to seller

```python
r = requests.post(f"{BASE_URL}/market/orders/{order_id}/messages",
    headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
    json={"content": "Can you also include ETH data?"})
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
Freezes payment. Admin will review and decide fund allocation.

```python
r = requests.post(f"{BASE_URL}/market/orders/{order_id}/dispute",
    headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
    json={"reason": "Seller delivered wrong data"})
```
Confidence
70% 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 skill collects a full shipping address and transmits it to an external service without any explicit privacy notice, minimization guidance, or handling safeguards. This creates unnecessary privacy and compliance risk because users may disclose sensitive personal data without being informed how it is stored, used, or protected.

External Transmission

Medium
Category
Data Exfiltration
Content
```python
# ⚠️ ASK THE USER for their shipping details
r = requests.put(f"{BASE_URL}/market/address",
    headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
    json={"address": {
        "name": "John Doe", "street": "123 Main St",
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.