T09 · Insecure Skill Coding Practices
- Location
scripts/onboarding.py:344- Finding
Undisclosed and Unnecessary Transmission of Client Contact Email to a Third-Party LLM
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly matches its stated onboarding-tracker purpose, but it can send a client's contact email and onboarding status to MiniMax without clearly disclosing that full data sharing.
Review this skill before installing if your client names, email addresses, contract values, or onboarding status are confidential. Use it only with data you are willing to store locally in plaintext, and avoid setting MINIMAX_API_KEY unless you are comfortable sending reminder-generation context, including contact email in the current implementation, to MiniMax.
scripts/onboarding.py:344Undisclosed and Unnecessary Transmission of Client Contact Email to a Third-Party LLM
scripts/onboarding.py:72Sensitive Client Records Are Created Without Explicit Restrictive File Permissions
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
author: "NASSER AL-SOLAITTI"
description: "Local client onboarding tracker. Store client records, track onboarding steps, and generate reminder text via LLM. Use when user says 'track a new client', 'add client onboarding', 'show onboarding status', or 'onboarding checklist'.
permissions:
- filesystem-write: write onboarding and config data to ~/.openclaw/smb-client-onboarding/
- env: read MINIMAX_API_KEY from environment (for optional LLM reminder generation)
- network: send client name and onboarding step context to minimax LLM API when reminder generation is used
privacy: |
The skill includes an external LLM integration that transmits onboarding-derived content to a third-party service, even though the primary CLI purpose is onboarding workflow management rather than external AI processing. This expands the data exposure surface and can leak client-related business information outside the local tool's expected trust boundary.
This code establishes network transmission to a third-party LLM endpoint and is later used to send onboarding-related client data. The transmission itself is intentional and over HTTPS, but in this skill context it creates a real confidentiality risk because customer data leaves the local environment for a service not clearly required for core onboarding operations.
api_key, _ = get_llm_api_key()
if not api_key:
return None
base_url = "https://api.minimax.chat/v1" # pinned, no env override
try:
data = json.dumps({
"model": "minimax/MiniMax-M3",
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"gmail": ("https://console.cloud.google.com", "GMAIL_CLIENT_ID + GMAIL_CLIENT_SECRET"),
"stripe": ("https://dashboard.stripe.com/apikeys", "STRIPE_API_KEY"),
"hubspot": ("https://app.hubspot.com/private-apps", "HUBSPOT_API_KEY"),
"slack": ("https://api.slack.com/apps", "SLACK_BOT_TOKEN"),
"trello": ("https://trello.com/app-key", "TRELLO_API_KEY + TRELLO_TOKEN"),
}
At reminder-generation time, the tool sends client name, contact, and stuck onboarding steps to an external LLM without a prominent user-facing warning or consent check. In a business onboarding context, this is sensitive customer and pipeline data, so silent transmission increases privacy and compliance risk.
Client-provided identifiers and onboarding status details are interpolated directly into the LLM prompt and sent off-host. Even if not code-execution dangerous, this is sensitive data disclosure to a third party and could expose personal contact details, business relationships, and operational status information.
The command name and help text say a step will be skipped, but the implementation marks it as complete and records completed_at. This can falsify workflow state, suppress pending-task visibility, and create inaccurate audit/history records for client onboarding steps such as document collection or payment-related tasks.
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.
level = logging.INFO
env_level = os.environ.get("CLAWHUB_LOG_LEVEL", "").upper()
if env_level in ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"):
level = getattr(logging, env_level)
logging.basicConfig(
level=level,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
No suspicious patterns detected.