Back to skill

Security audit

DingTalk Channel Install

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says, but its default setup exposes the OpenClaw agent broadly over DingTalk and stores credentials without enough safeguards.

Review before installing. Use an explicit DingTalk allowlist instead of allowFrom ["*"], restrict DM and group policies where possible, pin the @soimy/dingtalk plugin version, and protect or rotate the DingTalk client secret if it has been entered on a command line or stored in a broadly readable OpenClaw config file.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (3)

T08 · Insecure Dependencies

Error
Location
scripts/install_dingtalk.py:145
Finding
Unpinned Third-Party Plugin Installation Creates a Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install_dingtalk.py:145-148` **Additional Location**: `SKILL.md:28` **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: High ### Vulnerable Code ```python if not run_command( ["openclaw", "plugins", "install", "@soimy/dingtalk"], "安装钉钉插件" ): ``` The documented manual installation uses the same unpinned package reference: ```bash openclaw plugins install @soimy/dingtalk ``` ### Technical Analysis The installer retrieves `@soimy/dingtalk` without specifying an exact reviewed version or an integrity hash. Consequently, the code installed during one execution may differ from the code reviewed during the audit. Although invoking the OpenClaw package installer is consistent with the Skill's stated purpose, relying on the package's current registry release creates a supply-chain trust boundary. A compromised package publisher, registry account, dependency release, or unexpected upstream update could introduce arbitrary plugin behavior. The audit found no evidence that the Skill intentionally installs a malicious package. The vulnerability arises from the inability to guarantee that future installations will retrieve the reviewed code. ### Attack Path 1. An attacker compromises the package publisher, registry account, or upstream release process. 2. The attacker publishes a malicious release under the legitimate `@soimy/dingtalk` package name. 3. A user executes this Skill after the malicious release becomes current. 4. The unpinned installation command retrieves and installs the attacker-controlled version. 5. OpenClaw enables the plugin and subsequently loads it as part of gateway operation. 6. The malicious plugin executes with the permissions available to the OpenClaw process. ### Impact Assessment A compromised plugin could operate with the OpenClaw process's local privileges. Depending on that process's permissions and exposed APIs, this may allo ...[truncated 454 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the plugin to an exact, reviewed version, for example: ```python ["openclaw", "plugins", "install", "@soimy/dingtalk@<reviewed-version>"] ``` 2. Where supported, verify the package against a trusted integrity hash or signed provenance record before activation. 3. Use an organization-controlled lockfile or approved dependency manifest. 4. Restrict installation to an explicitly trusted registry and reject unexpected registry overrides. 5. Review each version update before changing the pinned version. 6. Install and test the plugin in a restricted environment before enabling it in a production gateway. 7. Run OpenClaw under a dedicated least-privilege service account to reduce the impact of a compromised plugin. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/install_dingtalk.py:59
Finding
DingTalk Channel Is Configured to Accept Messages from Every Sender<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install_dingtalk.py:59-67` **Additional Location**: `SKILL.md:39-46` **Vulnerability Type**: Unrestricted channel authorization **Risk Level**: High ### Vulnerable Code ```python dingtalk_config = { "enabled": True, "clientId": client_id, "clientSecret": client_secret, "dmPolicy": "open", "groupPolicy": "open", "showThinking": True, "thinkingMessage": "🤔 思考中,请稍候...", "debug": False, "messageType": kwargs.get('message_type', 'markdown'), "allowFrom": ["*"] } ``` ### Technical Analysis The generated configuration enables both direct-message and group-message access through the `open` policies and combines them with the wildcard authorization entry `allowFrom: ["*"]`. This configuration does not establish an explicit trusted-sender boundary. Any DingTalk identity or group capable of reaching the configured bot may be accepted by the channel. If the connected OpenClaw agent exposes tools, confidential context, or privileged workflows, an unauthorized sender could submit instructions to those capabilities. This does not by itself prove that every external DingTalk user can discover or reach the bot; DingTalk-side application and organization controls may provide additional restrictions. However, the Skill removes the OpenClaw-side sender restriction and therefore relies entirely on external controls. ### Attack Path 1. The user runs the installer, which writes `dmPolicy: "open"`, `groupPolicy: "open"`, and `allowFrom: ["*"]`. 2. The gateway restarts and activates the DingTalk channel with those settings. 3. An unauthorized user gains the ability to contact the bot directly or from a group, subject to DingTalk-side reachability. 4. The wildcard source rule accepts the attacker's messages. 5. The attacker submits requests or prompt-injection content intended to invoke agent tools, disclose contextual information, or influence connected workflows. 6. The a ...[truncated 690 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use restrictive defaults: ```python "dmPolicy": "restricted", "groupPolicy": "restricted", "allowFrom": [] ``` 2. Require the user to supply explicit DingTalk user, group, or organization identifiers before enabling the channel. 3. Validate allowlist entries and reject the wildcard unless the user provides explicit confirmation acknowledging the exposure. 4. Separate direct-message and group-message allowlists where supported. 5. Apply least privilege to all tools available to agents reachable from the messaging channel. 6. Add rate limits, authentication logging, and alerts for unexpected sender identities. 7. Document the DingTalk-side controls required to restrict bot discovery, group membership, and application access. 8. Consider leaving the channel disabled until a non-empty trusted-sender allowlist has been configured. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/install_dingtalk.py:98
Finding
Client Secret Is Accepted Through the Command Line and Stored Without Permission Hardening<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install_dingtalk.py:98-120` **Additional Location**: `SKILL.md:20-23` **Vulnerability Type**: Plaintext credential exposure **Risk Level**: Medium ### Vulnerable Code The secret is accepted as a command-line argument: ```python parser.add_argument('--client-id', required=True, help='钉钉应用 Client ID') parser.add_argument('--client-secret', required=True, help='钉钉应用 Client Secret') ``` The supplied credential is placed into the channel configuration: ```python dingtalk_config = { "enabled": True, "clientId": client_id, "clientSecret": client_secret, "dmPolicy": "open", "groupPolicy": "open", "showThinking": True, "thinkingMessage": "🤔 思考中,请稍候...", "debug": False, "messageType": kwargs.get('message_type', 'markdown'), "allowFrom": ["*"] } ``` The complete configuration, including the plaintext secret, is written without verifying or tightening file permissions: ```python with open(config_path, 'w') as f: json.dump(config, f, indent=2) ``` The documented invocation also places the secret directly in the command line: ```bash python3 ~/.openclaw/workspace/my-skills/skills/dingtalk-channel-install/scripts/install_dingtalk.py \ --client-id <你的 Client ID> \ --client-secret <你的 Client Secret> ``` ### Technical Analysis Command-line secrets can be retained in shell history and may be visible through process-inspection mechanisms while the command is running, depending on operating-system and account isolation rules. The script also stores `clientSecret` directly in the JSON configuration. While application credentials may need persistent storage for gateway operation, the script neither verifies that the existing file is owner-only nor applies restrictive permissions after writing it. Python's `open(..., "w")` preserves the permission mode of an existing file, so an already over-permissive OpenClaw configuration remains over-permissive. The scrip ...[truncated 1558 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not require secrets on the command line. Accept the secret through an interactive hidden prompt: ```python from getpass import getpass client_secret = getpass("DingTalk Client Secret: ") ``` 2. Alternatively, integrate with an operating-system keyring, secret manager, or protected file descriptor. 3. If environment variables are supported for automation, clearly document their exposure limitations and avoid printing the environment. 4. Inspect the configuration file's ownership and permissions before writing. Reject files owned by another user or files accessible to group/other users. 5. Enforce owner-only access after writing: ```python import os os.chmod(config_path, 0o600) ``` 6. Use an atomic write through a temporary file created with mode `0600`, then replace the target after successful serialization. 7. Avoid displaying or logging argument values and sanitize error reporting. 8. Document credential rotation procedures and advise users to rotate any secret previously entered directly into shell history. 9. Where OpenClaw supports secret references, store only a reference in `openclaw.json` and keep the credential in a dedicated secret store. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (7)

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill instructs users to run shell commands and a Python installer that will read and write configuration files, but it does not declare any tool scope such as permissions or allowed-tools. This weakens reviewability and containment because an agent or user cannot easily tell up front that the skill requires shell execution and config file modification.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The sample configuration sets `allowFrom: ["*"]`, which permits messages from all sources, but the skill does not clearly warn that this broadens who can interact with the DingTalk channel. In a messaging gateway context, this can expose the bot to unsolicited or unauthorized conversations, increasing the risk of abuse, data exposure, or unintended command execution through the channel.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The script's user-facing documentation and console messages are written in Chinese only, which imposes a specific language on all users without offering opt-in or alternative locale support. The file does not document that this tool is intentionally limited to a Chinese-speaking or region-specific audience.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 将命令字符串转换为列表形式以避免 shell 注入
    if isinstance(cmd, str):
        cmd = cmd.split()
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        print(f"❌ 失败:{result.stderr}")
        return False
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The script configures allowFrom as ['*'], which permits messages from any DingTalk sender instead of restricting access to expected users, groups, or tenants. In the context of an agent channel installation tool, this broadens the exposed attack surface and can allow unauthorized parties to interact with the agent, potentially triggering sensitive actions or data exposure through the connected system.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script stores the DingTalk client ID and client secret directly in a JSON config file without any warning, permission tightening, or safer secret-handling path. If the file is readable by other local users, checked into version control, included in backups, or exposed through support tooling, the credentials could be recovered and abused to impersonate or access the DingTalk integration.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def restart_gateway():
    """重启 Gateway"""
    print("\n🔄 重启 Gateway...")
    result = subprocess.run(
        ["openclaw", "gateway", "restart"],
        capture_output=True,
        text=True
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.