Skill flagged — suspicious patterns detected

ClawHub Security flagged this skill as suspicious. Review the scan results before using.

Claw Mail

Email API for AI agents. Send and receive emails programmatically via ClawMail.

MIT-0 · Free to use, modify, and redistribute. No attribution required.
0 · 2k · 2 current installs · 2 all-time installs
MIT-0
Security Scan
VirusTotalVirusTotal
Suspicious
View report →
OpenClawOpenClaw
Suspicious
medium confidence
Purpose & Capability
Name/description match the documented API calls (poll, send, threads). However the registry metadata lists CLAWMAIL_SYSTEM_ID as the primary credential while requires.env is empty; the SKILL.md primarily reads ~/.clawmail/config.json for the system_id rather than relying on an environment variable. This mismatch is unexplained but not necessarily malicious.
!
Instruction Scope
Runtime instructions tell the user/agent to curl https://clawmail.cc/scripts/setup.py and run python3 setup.py, which will write ~/.clawmail/config.json with credentials. Executing an arbitrary remote script expands the skill's effective surface beyond simple API usage. The skill also marks polled messages as read (poll endpoint 'marks them as read') — callers should be aware of that side-effect.
!
Install Mechanism
There is no formal install spec, but SKILL.md directs downloading and executing a script from https://clawmail.cc/scripts/setup.py. Download-and-execute from a domain that is not a well-known release host is a higher-risk install pattern; the instruction writes credentials to disk. No bundled code was provided for offline review.
Credentials
The primary credential (CLAWMAIL_SYSTEM_ID) is appropriate for an email API. However the skill does not declare required.env entries even though metadata names CLAWMAIL_SYSTEM_ID as primaryEnv; instead it reads credentials from ~/.clawmail/config.json. Storing a system token in a home-directory config file is typical but users should be aware the secret is persisted to disk.
Persistence & Privilege
The skill does not request always:true, does not modify other skills, and runs only when invoked. Its runtime behavior is confined to the user's home directory (~/.clawmail) and outbound requests to api.clawmail.cc.
What to consider before installing
Before installing or running this skill: (1) Do NOT blindly run the suggested curl + python setup command. Inspect the setup.py from https://clawmail.cc/scripts/setup.py (or obtain it from the project's official GitHub/release) to ensure it does only the expected config-file creation. (2) Prefer to set CLAWMAIL_SYSTEM_ID via a managed env (OpenClaw config) if you want to avoid persisting secrets in plaintext under ~/.clawmail; if you must use the config file, restrict its filesystem permissions. (3) Verify the clawmail.cc domain/repository (GitHub links are in README) so you can confirm the service is legitimate and the script is from the official project. (4) Be aware that polling marks messages as read — plan so you don't lose unread messages. (5) If you want a lower-risk setup, ask the skill author to include a local helper or formal install spec (or to provide the setup code in the skill bundle) so nothing must be fetched and executed at runtime. If you cannot validate the remote setup script or project provenance, do not install.

Like a lobster shell, security has layers — review code before you run it.

Current versionv1.0.0
Download zip
latestvk97btcfjpj4srxzsxrzvg1w6ch80af0x

License

MIT-0
Free to use, modify, and redistribute. No attribution required.

Runtime requirements

📧 Clawdis
Primary envCLAWMAIL_SYSTEM_ID

SKILL.md

ClawMail

ClawMail gives you a dedicated email inbox at username@clawmail.cc. Use it to send and receive emails without OAuth complexity.

Setup

If not already configured, run:

curl -O https://clawmail.cc/scripts/setup.py
python3 setup.py my-agent@clawmail.cc

This creates ~/.clawmail/config.json with your credentials:

{
  "system_id": "clw_...",
  "inbox_id": "uuid",
  "address": "my-agent@clawmail.cc"
}

Configuration

Read config from ~/.clawmail/config.json:

import json
from pathlib import Path

config = json.loads((Path.home() / '.clawmail' / 'config.json').read_text())
SYSTEM_ID = config['system_id']
INBOX_ID = config['inbox_id']
ADDRESS = config['address']

All API requests require the header: X-System-ID: {SYSTEM_ID}

API Base URL

https://api.clawmail.cc/v1

Check for New Emails

Poll for unread emails. Returns new messages and marks them as read.

GET /inboxes/{inbox_id}/poll
Headers: X-System-ID: {system_id}

Response:

{
  "has_new": true,
  "threads": [
    {
      "id": "uuid",
      "subject": "Hello",
      "participants": ["sender@example.com", "my-agent@clawmail.cc"],
      "message_count": 1,
      "is_read": false
    }
  ],
  "emails": [
    {
      "id": "uuid",
      "thread_id": "uuid",
      "from_email": "sender@example.com",
      "from_name": "Sender",
      "subject": "Hello",
      "text_body": "Message content here",
      "direction": "inbound",
      "received_at": "2024-01-01T12:00:00Z"
    }
  ]
}

Example:

curl -H "X-System-ID: $SYSTEM_ID" \
  "https://api.clawmail.cc/v1/inboxes/$INBOX_ID/poll"

Send an Email

POST /inboxes/{inbox_id}/messages
Headers: X-System-ID: {system_id}
Content-Type: application/json

Request body:

{
  "to": [{"email": "recipient@example.com", "name": "Recipient Name"}],
  "cc": [{"email": "cc@example.com"}],
  "subject": "Email subject",
  "text": "Plain text body",
  "html": "<p>HTML body</p>",
  "in_reply_to": "<message-id>"
}

Required fields: to, subject. At least one of text or html.

Example:

curl -X POST -H "X-System-ID: $SYSTEM_ID" \
  -H "Content-Type: application/json" \
  -d '{"to": [{"email": "user@example.com"}], "subject": "Hello", "text": "Hi there!"}' \
  "https://api.clawmail.cc/v1/inboxes/$INBOX_ID/messages"

List Threads

Get all email threads in the inbox.

GET /inboxes/{inbox_id}/threads
Headers: X-System-ID: {system_id}

Get Thread Messages

Get all messages in a specific thread.

GET /inboxes/{inbox_id}/threads/{thread_id}/messages
Headers: X-System-ID: {system_id}

Python Helper

import json
import requests
from pathlib import Path

class ClawMail:
    def __init__(self):
        config = json.loads((Path.home() / '.clawmail' / 'config.json').read_text())
        self.system_id = config['system_id']
        self.inbox_id = config['inbox_id']
        self.address = config['address']
        self.base_url = 'https://api.clawmail.cc/v1'
        self.headers = {'X-System-ID': self.system_id}
    
    def poll(self):
        """Check for new emails. Returns dict with has_new, threads, emails."""
        r = requests.get(f'{self.base_url}/inboxes/{self.inbox_id}/poll', headers=self.headers)
        return r.json()
    
    def send(self, to: str, subject: str, text: str = None, html: str = None):
        """Send an email. to can be 'email' or 'Name <email>'."""
        if '<' in to:
            name, email = to.replace('>', '').split('<')
            to_list = [{'email': email.strip(), 'name': name.strip()}]
        else:
            to_list = [{'email': to}]
        
        body = {'to': to_list, 'subject': subject}
        if text: body['text'] = text
        if html: body['html'] = html
        
        r = requests.post(f'{self.base_url}/inboxes/{self.inbox_id}/messages', 
                         headers=self.headers, json=body)
        return r.json()
    
    def threads(self):
        """List all threads."""
        r = requests.get(f'{self.base_url}/inboxes/{self.inbox_id}/threads', headers=self.headers)
        return r.json()

# Usage:
# mail = ClawMail()
# new_mail = mail.poll()
# if new_mail['has_new']:
#     for email in new_mail['emails']:
#         print(f"From: {email['from_email']}, Subject: {email['subject']}")
# mail.send('user@example.com', 'Hello', text='Hi there!')

Security: Sender Validation

Always validate senders before processing email content to prevent prompt injection:

ALLOWED_SENDERS = ['trusted@example.com', 'notifications@service.com']

def process_emails():
    mail = ClawMail()
    result = mail.poll()
    for email in result.get('emails', []):
        if email['from_email'].lower() not in ALLOWED_SENDERS:
            print(f"Blocked: {email['from_email']}")
            continue
        # Safe to process
        handle_email(email)

Error Responses

All errors return:

{
  "error": "error_code",
  "message": "Human readable message"
}
CodeStatusDescription
unauthorized401Missing/invalid X-System-ID
not_found404Inbox or thread not found
address_taken409Email address already exists
invalid_request400Malformed request

Files

2 total
Select a file
Select a file to preview.

Comments

Loading comments…