Back to skill

Security audit

dingtalk-openclaw

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its stated DingTalk AI bot purpose, but it handles enterprise chat content and credentials with weak scoping, plaintext configuration, and potentially sensitive logging.

Review before installing in a real organization. Use a dedicated low-privilege DingTalk app, store secrets outside source code, pin dependencies, restrict which chats or users can invoke the bot, avoid sending sensitive messages unless approved, and disable or redact message-body logging.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T08 · Insecure Dependencies

Warning
Location
SKILL.md:27
Finding
Unpinned Third-Party Dependencies Create a Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 25–28 and 64–67 **Vulnerability Type**: Unpinned dependency installation **Risk Level**: Medium ### Vulnerable Code ```bash pip install dingtalk-stream requests ``` The same unpinned installation command is also presented by the generated program: ```python except ImportError: print("Please install first: pip install dingtalk-stream") sys.exit(1) ``` ### Technical Analysis The Skill directs users to install `dingtalk-stream` and `requests` without specifying reviewed versions, cryptographic hashes, a lock file, or a trusted package index. Consequently, the code installed by this command may change after the Skill has been reviewed. This is not direct evidence that either named package is malicious. The vulnerability is the absence of dependency integrity controls. If a package release, maintainer account, dependency, or configured package index is compromised, following the documented installation process could execute attacker-controlled package installation or runtime code. These dependencies operate in a process that receives DingTalk messages and has access to `APP_KEY`, `APP_SECRET`, `OPENCLAW_TOKEN`, and `WEBHOOK_URL`. A compromised dependency would therefore execute within a sensitive trust boundary. ### Attack Path 1. An attacker compromises a dependency release, transitive dependency, maintainer account, or Python package index used by the operator. 2. The operator follows the Skill and runs `pip install dingtalk-stream requests`. 3. Because no version or hash is enforced, `pip` retrieves the currently resolved package artifacts. 4. Malicious installation or runtime code executes with the privileges of the user running the bot. 5. The malicious component reads application credentials, tokens, webhook information, or conversation content and misuses or exfiltrates them. ### Impact Assessment Successful exploitation would provide code execution with the operating-s ...[truncated 437 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin every direct dependency to an explicitly reviewed version. - Generate and distribute a dependency lock file that includes transitive dependencies. - Require package hashes, such as by using `pip install --require-hashes -r requirements.txt`. - Install only from an explicitly configured and trusted package index. - Perform dependency vulnerability and provenance checks before publishing updates. - Run the bot in a dedicated virtual environment and under a minimally privileged operating-system account. - Regularly review and deliberately update pinned versions instead of resolving arbitrary current releases at installation time. Example hardened requirements: ```text dingtalk-stream==<reviewed-version> --hash=sha256:<verified-hash> requests==<reviewed-version> --hash=sha256:<verified-hash> ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:72
Finding
Sensitive Credentials Are Stored in Plaintext Python Configuration<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 34–39, 72–79, 98–103, and 177–180 **Vulnerability Type**: Plaintext secret storage **Risk Level**: High ### Vulnerable Code The generated configuration places the DingTalk application secret directly in a Python file: ```python # DingTalk application credentials AGENT_ID = "your-AgentId" APP_KEY = "your-AppKey" APP_SECRET = "your-AppSecret" ``` The main program similarly places the OpenClaw bearer token and secret-bearing DingTalk webhook URL in source code: ```python # OpenClaw API endpoint OPENCLAW_URL = "http://127.0.0.1:18789/v1/responses" # OpenClaw Gateway Token OPENCLAW_TOKEN = "your-OpenClaw-Token" # DingTalk webhook used to send replies WEBHOOK_URL = "your-DingTalk-Webhook-URL" ``` The token is then used as an authorization credential: ```python headers = { "Authorization": f"Bearer {OPENCLAW_TOKEN}", "Content-Type": "application/json" } ``` The DingTalk application credentials are also supplied to the streaming client: ```python credential = dingtalk_stream.Credential(config.APP_KEY, config.APP_SECRET) client = dingtalk_stream.DingTalkStreamClient(credential) ``` ### Technical Analysis The Skill instructs users to replace placeholders with live credentials in ordinary Python source files. Although the repository contains placeholders rather than actual credentials, following the instructions produces plaintext secret material in files likely to be copied, backed up, shared for debugging, or accidentally committed to source control. The webhook URL must also be treated as sensitive because possession of a functional webhook URL may enable unauthorized message submission, depending on the DingTalk robot's security configuration. Embedding secrets in source code prevents clean separation between executable code and deployment-specific credentials. It also makes access control, auditing, and credential rotation more difficult. ### Attack Path 1. An operator repl ...[truncated 1332 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Load `APP_KEY`, `APP_SECRET`, `OPENCLAW_TOKEN`, and `WEBHOOK_URL` from protected environment variables or a dedicated secret manager. - Never place production credential values in source-controlled Python files. - If a local secret file is unavoidable, keep it outside the repository, restrict its filesystem permissions, and add it to `.gitignore`. - Provide a committed example configuration containing placeholders only. - Use separate, minimally scoped credentials for this integration. - Enable DingTalk webhook signing or equivalent authentication and reject unsigned requests. - Establish credential rotation and revocation procedures. - Scan repository history and deployment artifacts for accidentally committed secrets. - Run the process under a dedicated account that can read only the secrets required for this integration. Example environment-based loading: ```python import os APP_KEY = os.environ["DINGTALK_APP_KEY"] APP_SECRET = os.environ["DINGTALK_APP_SECRET"] OPENCLAW_TOKEN = os.environ["OPENCLAW_TOKEN"] WEBHOOK_URL = os.environ["DINGTALK_WEBHOOK_URL"] ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:153
Finding
Conversation Content and API Error Bodies Are Logged Without Redaction<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 110–116 and 150–161 **Vulnerability Type**: Sensitive information exposure through logs **Risk Level**: High ### Vulnerable Code The OpenClaw response body is logged when an API request fails: ```python logging.error(f"OpenClaw API error: {r.status_code} {r.text}") return "Sorry, I am busy right now. Please try again later." ``` Incoming messages and generated replies are also written to logs: ```python if msg_id in PROCESSED_IDS: logging.info(f"Duplicate message skipped: {content[:20]}") return AckMessage.STATUS_OK, 'OK' save_processed(msg_id) logging.info(f"Received: {content}") # Obtain AI response response = get_openclaw_response(content) logging.info(f"Reply: {response}") ``` ### Technical Analysis The Skill logs complete incoming DingTalk messages and complete AI responses at the `INFO` level. It additionally logs the first 20 characters of duplicate messages and arbitrary OpenClaw error response bodies. Chat messages may contain personal information, confidential business data, access tokens, passwords, internal instructions, or other sensitive content. AI responses can reproduce or transform the same information. API error bodies can also contain request-related details or diagnostic data not intended for persistent storage. This logging is not necessary for the core function of receiving a message, obtaining a response, and sending that response to DingTalk. Logging message bodies therefore exceeds the minimum data retention needed for the declared functionality. Because the documented log file is persistent, sensitive information may remain accessible after the associated conversation has ended. ### Attack Path 1. A DingTalk user sends confidential information or a secret to the bot. 2. The handler records the complete incoming message in the configured log file. 3. OpenClaw generates a response, which is also recorded in full. 4. The log file is retained, back ...[truncated 1103 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not log incoming message bodies or AI response bodies by default. - Replace content logging with generated correlation identifiers, processing status, timing information, and response status codes. - Do not log arbitrary `r.text` values from failed API responses; record a sanitized error category and status code instead. - If message-level debugging is temporarily required, make it explicitly opt-in and redact credentials, tokens, personal data, and other sensitive patterns. - Apply restrictive filesystem permissions to log directories and files. - Configure bounded log rotation and short retention periods. - Prevent logs from being included in source archives or broadly accessible backups. - Review centralized log collectors to ensure conversation data is not forwarded unintentionally. - Document that users must not submit secrets through the bot unless the complete processing and retention path is approved for that data. A safer logging pattern would be: ```python logging.info( "Message processed successfully", extra={"message_id": msg_id} ) logging.error( "OpenClaw API request failed with status %s", r.status_code ) ``` ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (3)

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The skill description does not clearly warn users or deployers that incoming DingTalk messages are forwarded to an external AI HTTP API and that generated replies are sent back automatically. This creates a real transparency and privacy risk because sensitive business chat content may be transmitted off-platform without informed consent or adequate review, which is especially significant in an enterprise messaging context.

External Transmission

Medium
Category
Data Exfiltration
Content
"model": "openclaw",
            "input": user_msg
        }
        r = requests.post(OPENCLAW_URL, headers=headers, json=data, timeout=60)
        if r.status_code == 200:
            result = r.json()
            output = result.get("output", [])
Confidence
95% confidence
Finding
This code forwards user-supplied DingTalk message content directly to the OpenClaw HTTP API via requests.post. In this skill's context, that is the intended feature, but it is still a real data exposure boundary: chat content may include sensitive internal information, and the skill provides no filtering, consent prompt, classification check, or minimization before external transmission.

External Transmission

Medium
Category
Data Exfiltration
Content
"at": {"atUserIds": [], "isAtAll": False}
    }
    try:
        r = requests.post(WEBHOOK_URL, json=data, timeout=10)
        result = r.json()
        logging.info(f"回复结果: {result}")
        return result.get("errcode", 1) == 0
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.