Back to skill

Security audit

Dingtalk CLI SKILL

Security checks for vulnerabilities and agentic risk

Overview

This DingTalk automation skill is not clearly malicious, but it needs review because it combines broad workplace data and mutation authority with under-scoped safeguards and risky install guidance.

Install only in an organization that trusts the dws CLI publisher and the DingTalk permissions being granted. Prefer the pinned SKILL.md installer path, verify the SHA-256, avoid the README's mutable main-branch installer, store client secrets in a secret manager, and require human confirmation before deletes, approvals, broadcasts, @all messages, attendance/contact lookups, report sends, and attachment uploads.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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)

T03 · Remote Payload Retrieval and Execution

Error
Location
README.md:17
Finding
Mutable Remote PowerShell Installer Is Downloaded and Executed<![CDATA[ ## Vulnerability Details **File Location**: `README.md:17-24` **Vulnerability Type**: `T03: Remote Payload Retrieval and Execution` **Risk Level**: High ### Vulnerable Code ```powershell # Step 1: Download the installation script locally $scriptPath = "$env:TEMP\install_dws.ps1" Invoke-WebRequest -Uri "https://raw.githubusercontent.com/DingTalk-Real-AI/dingtalk-workspace-cli/main/scripts/install.ps1" -OutFile $scriptPath -UseBasicParsing # Step 2: Open and review it notepad $scriptPath # Step 3: Execute it after deciding that it is trusted & $scriptPath ``` ### Technical Analysis The installation instructions download a PowerShell script from the mutable `main` branch of an external GitHub repository and subsequently execute it. The downloaded installer is not pinned to an immutable commit, and the instructions do not perform cryptographic checksum or publisher-signature verification. Although the documentation tells users to inspect the script manually, this is advisory rather than an enforced security control. Users may execute the script without a meaningful review, and even a manual review does not provide reproducible verification for later downloads. Because the effective installer payload can change after this Skill package has been audited, the behavior constitutes remote payload retrieval and execution. ### Attack Path 1. An attacker compromises the referenced repository, a maintainer account, or the `main` branch. 2. The attacker modifies `scripts/install.ps1` to include malicious PowerShell commands. 3. A user follows the documented installation procedure. 4. `Invoke-WebRequest` downloads the attacker-controlled version into the user’s temporary directory. 5. The user executes the downloaded script with `& $scriptPath`. 6. The malicious commands run with the privileges of the WorkBuddy or PowerShell user. ### Impact Assessment Successful exploitation provides arbitrary code execution under the invoking user account. Depending on ...[truncated 592 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the mutable `main` URL with an immutable commit URL or a versioned release artifact. 2. Publish a SHA-256 digest for the exact installer and enforce verification before execution: ```powershell $expectedHash = "<reviewed-sha256>" $actualHash = (Get-FileHash -Algorithm SHA256 $scriptPath).Hash.ToLower() if ($actualHash -ne $expectedHash) { throw "Installer checksum verification failed" } ``` 3. Prefer shipping the reviewed installer inside the Skill package rather than retrieving executable code at installation time. 4. Digitally sign the PowerShell script and verify the Authenticode signature and expected publisher. 5. Ensure installation does not require administrator privileges unless a specific operation strictly requires them. 6. Document the exact pinned version and update it only through a new reviewed Skill release. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:16
Finding
Privileged DingTalk Operations Depend on an External Precompiled Executable<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:16-25` **Vulnerability Type**: `T08: Insecure Dependencies` **Risk Level**: Medium ### Vulnerable Code ```yaml install: - kind: url url: https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.8/dws-windows-amd64.zip bins: - dws.exe verify: sha256: 7dc4e568b1386423784e6baf17fe675c11eb6c075bd887dcb0ede53cdded85e8 homepages: - https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/latest ``` Related inconsistent installation guidance appears at `README.md:31-34`: ```markdown Visit the GitHub Release page and download `dws-windows-amd64.zip`, extract `dws.exe`, and save it to `~\.local\bin\dws.exe`. SHA256 verification is optional: download `checksums.txt` and confirm that the hash of `dws-windows-amd64.zip` is `7dc4e568b1386423784e6baf17fe675c11eb6c075bd887dcb0ede53cdded85e8`. ``` ### Technical Analysis The Skill delegates authentication and all DingTalk operations to an external, precompiled `dws.exe`. This executable is a high-trust component because it handles OAuth authentication and can read or modify DingTalk contacts, calendars, messages, attendance records, reports, todos, and AI tables. The `SKILL.md` installer pins version `v1.0.8` and supplies a SHA-256 digest, which provides useful integrity protection. However: - No publisher or code-signing verification is required. - The executable’s implementation is not part of the audited package. - The README directs users to the mutable `releases/latest` page. - README checksum verification is described as optional. - A file downloaded from `latest` may not correspond to the documented `v1.0.8` checksum. Consequently, users following the manual documentation may install an unreviewed or substituted executable with access to sensitive business data. ### Attack Path 1. An attacker compromises the upstream release process, maintainer account, or release artifact. 2. ...[truncated 1475 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use the same immutable versioned artifact in all installation paths; remove directions to download from `releases/latest`. 2. Make SHA-256 verification mandatory rather than optional. 3. Obtain the expected digest from a channel independent of the artifact download location. 4. Require an operating-system code signature and verify the expected publisher before execution. 5. Publish reproducible build instructions, source provenance, and a software bill of materials for the CLI. 6. Pin the CLI version to the Skill version and require a new security review when updating it. 7. Request only the minimum DingTalk OAuth scopes needed for the user’s selected operations. 8. Avoid exposing `DWS_CLIENT_SECRET` to the CLI unless application-credential authentication is specifically required. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/upload_attachment.py:67
Finding
Attachment Contents Can Be Uploaded to an Unvalidated Destination<![CDATA[ ## Vulnerability Details **File Location**: `scripts/upload_attachment.py:67-75` and `scripts/upload_attachment.py:135-144` **Vulnerability Type**: `T09: Insecure Skill Coding Practices` **Risk Level**: High ### Vulnerable Code ```python def upload_to_oss(upload_url: str, file_path: Path, mime_type: str) -> bool: """Upload a file to OSS through HTTP PUT.""" file_data = file_path.read_bytes() req = Request(upload_url, data=file_data, method='PUT') req.add_header('Content-Type', mime_type) try: with urlopen(req, timeout=120) as resp: if resp.status == 200: return True ``` The destination is taken directly from the `dws` response: ```python upload_url = data.get('uploadUrl', '') file_token = data.get('fileToken', '') if not upload_url or not file_token: print(f"Error: response lacks uploadUrl or fileToken: {json.dumps(data, ensure_ascii=False)}", file=sys.stderr) return None # Step 2: PUT the file to OSS print(f"Step 2/3: Uploading file to OSS...", file=sys.stderr) if not upload_to_oss(upload_url, file_path, mime_type): return None ``` ### Technical Analysis Uploading the user-selected attachment is necessary for the declared AI-table attachment functionality. However, the script treats `uploadUrl` returned by the external `dws` process as trusted and sends the complete file contents to it without validating: - The URL scheme. - Whether TLS is required. - The destination hostname. - Whether the host belongs to an approved DingTalk or OSS domain. - Whether the URL contains an IP literal. - Whether the destination resolves to a loopback, private, link-local, or metadata-service address. - Whether redirects remain within the approved destination set. Python’s `urlopen` can follow redirects, so validation must also be applied to every redirect destination. The current implementation therefore grants the external CLI control over where a local file is transmitted. ### Attack Path ...[truncated 1687 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse the destination using `urllib.parse.urlsplit` and require the `https` scheme. 2. Maintain an explicit allowlist of documented DingTalk and OSS upload hostnames. Avoid permissive suffix checks that allow names such as `trusted.example.attacker.com`. 3. Reject URLs containing embedded credentials, unexpected ports, malformed hostnames, or IP-literal destinations. 4. Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and metadata-service addresses. 5. Disable automatic redirects or validate the scheme, hostname, port, and resolved addresses at every redirect hop. 6. Bind the accepted upload domains to official API documentation rather than accepting arbitrary hosts from the CLI response. 7. Ask for explicit user confirmation that identifies the local file and validated destination before transmitting sensitive attachments. 8. Stream the file rather than loading up to 100 MB entirely into memory; this reduces denial-of-service and memory-pressure risk. 9. Preserve the existing file-size limit and consider a lower default appropriate to expected attachment sizes. 10. Add automated tests covering HTTP URLs, attacker-controlled domains, IP literals, private addresses, DNS rebinding scenarios, and cross-domain redirects. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (58)

Hidden Instructions

High
Category
Prompt Injection
Content
---
name: dingtalk-dws
description: "Dingtalk CLI SKILL / 钉钉 dingding / dingtalk dws skill — Manage DingTalk products (AI forms, calendar, contacts, bots, todos, approvals, attendance, reports, DING, workbench). Manage DingTalk products: AI表格、日历、通讯录、群聊机器人、待办、审批、考勤、日报周报、DING消息、工作台"
version: "1.0.12"
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Credential Access

High
Category
Privilege Escalation
Content
| Token | 有效期 | 说明 |
|-------|--------|------|
| Access Token | 2 小时 | 调用 API 的凭证,过期自动刷新 |
| Refresh Token | 30 天 | 换新 Access Token,使用后轮转 |

30 天内使用一次即自动续期。
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
| Token | 有效期 | 说明 |
|-------|--------|------|
| Access Token | 2 小时 | 调用 API 的凭证,过期自动刷新 |
| Refresh Token | 30 天 | 换新 Access Token,使用后轮转 |

30 天内使用一次即自动续期。
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The document is written entirely in Chinese and directs agents to consult it when errors occur, but it does not offer any language choice or indicate that the Chinese-only format is a justified region-specific constraint. This can violate language/locale policy where users or agents are expected to operate in their preferred language unless they opt in.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The document instructs users to place OAuth client credentials in environment variables but does not warn that these values are sensitive secrets, may leak through shell history, process inspection, CI logs, or misconfigured debugging, and should be scoped and rotated carefully. In a CI/CD and headless-auth context, this omission materially increases the chance of credential exposure through operational misuse rather than direct code execution.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The entire guide is written in Chinese and presents all user utterance examples, routing instructions, and command guidance only in Chinese. This imposes a specific language/locale on skill behavior without any stated opt-in, alternative language support, or justification for a China-specific scope.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The document exposes high-impact operations including create, update, and irreversible delete for bases, tables, fields, and records, but does not require a consistent confirmation gate or impact preview before execution. This makes accidental or prompt-induced destructive actions more likely, especially because the same skill also documents the exact IDs and workflows needed to perform deletion quickly.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The intent-mapping section uses broad trigger words such as '表格/数据/查看/修改/删除', which can overlap with ordinary conversation and cause the agent to select this skill when the user did not clearly intend AI table operations. In a skill that supports destructive actions like delete and update, ambiguous invocation increases the chance of unintended data access or modification.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation exposes commands for retrieving individual attendance records, summaries, and team shift data, which are sensitive HR/personnel data, but it provides no privacy warning, authorization guidance, or role-based access constraints. In an agent setting, this can normalize broad access to employee attendance information and increase the chance of unauthorized collection or disclosure of personal data.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The intent mappings use very broad natural-language triggers such as '打卡记录/出勤/考勤' and '排班/班次/当班' without requiring identity, scope, or confirmation constraints. This can cause an agent to over-trigger attendance commands from ordinary conversation and potentially query sensitive employee data for unintended users or teams.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The intent-mapping section uses broad natural-language trigger phrases such as '日程/会议/约会/日历', '查看', and '有空吗/忙不忙/闲忙' to route users into calendar actions. In an agentic context, these generic phrases can overlap with ordinary conversation and cause unintended execution of read or write operations such as listing events, creating meetings, or querying availability without sufficiently explicit user confirmation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The webhook messaging guidance instructs use of a raw `--token <robotToken>` and shows examples for broadcasting alerts, including `--at-all`, but provides no warning that the token is a sensitive secret or that use of the webhook can trigger external message delivery. In practice, this can lead to credential exposure in prompts, logs, transcripts, screenshots, or command history, enabling unauthorized parties to send spoofed or disruptive messages to enterprise chat recipients.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The intent-mapping section uses broad natural-language triggers like '帮我建个群', '改一下群名', and '让机器人在群里发通知', which can over-match casual conversation and cause the agent to select a state-changing command without sufficient disambiguation. In this skill, several mapped actions modify group membership, rename chats, or send messages, so an over-eager matcher increases the risk of unintended operations and social-engineering-driven misuse.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The documentation explicitly enables searching users by mobile number and bulk retrieval of user details or department members, but it provides no privacy, authorization, or least-privilege guidance. In an agentic environment, this can normalize high-volume directory lookups and personal data access, increasing the chance of employee enumeration or misuse of personally identifiable information.

Vague Triggers

Medium
Confidence
92% confidence
Finding
该 markdown 文件包含“意图判断”规则,适用 SQP-1。像“找人/搜人”“哪个部门”“部门有谁/部门成员”这类短语非常常见,且未说明触发边界、适用上下文或排除条件,容易把普通对话误判为技能调用。

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The cross-product mapping instructs consumers to extract orgAuthEmail from contact data and reuse it in a mail-sending workflow, but it does not mention consent, purpose limitation, or data-sharing controls. This creates a realistic path for unnecessary propagation of directory-derived personal data into other systems, enabling spam, misdelivery, or broader privacy violations.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The documentation explicitly supports creating a report and sending it to chat recipients or specific user IDs, but it does not warn about disclosure risks or require explicit user acknowledgment of the recipients. In an agent workflow, this can lead to accidental sharing of sensitive work content with unintended recipients, especially when recipient IDs are provided programmatically or inferred from context.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The intent mapping uses broad trigger phrases such as “查日志/看日报” and “写日报/提交周报/发日志/填日志,” which can overlap with ordinary conversational language. In an agent setting, this can cause the wrong action path to be selected, including reading inbox reports or initiating report creation when the user’s intent was ambiguous, increasing the risk of unintended data access or actions.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This skill documents state-changing approval operations such as approve, reject, and revoke with no requirement for confirmation, no caution about side effects, and no guardrails around when those commands should be used. In an agent setting, that increases the risk that ambiguous user language or stale context could trigger unauthorized workflow actions affecting business processes.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The intent mapping is broad enough that common workplace phrases like '审批/请假/报销/出差' may route the agent into approval workflows without sufficient disambiguation between viewing information and taking action. In combination with the documented approve/reject/revoke commands and context passing of instance/task IDs, this can enable unintended or premature state-changing operations.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The intent mapping includes very broad, everyday phrases such as '记一下' and 'TODO' that can easily appear in normal conversation without an explicit request to create or modify a task. In an agent setting, this can cause unintended invocation of the todo skill and lead to accidental creation, modification, or deletion workflows if the system interprets ambiguous user utterances as commands.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger phrase for fetching application details is too vague because "应用详情" does not specify how the required appId should be obtained or validated before invoking `app get`. In an agent setting, this can cause incorrect tool selection, use of stale or inferred IDs from prior context, or unintended disclosure of details for the wrong application.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The file’s operative guidance is written entirely in Chinese, which imposes a language requirement on users and agents consuming the skill. The document does not provide an opt-in, alternative locale, or justification that this skill is region-specific, so it appears to violate the language/locale policy criterion.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The module docstring and usage text state the script queries '排班和出勤统计' (shifts and attendance statistics). However, the only backend operation performed is `dws attendance shift list`, and the output/empty-state messages also refer only to shift information, with no attendance-statistics retrieval or aggregation anywhere in the code.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The argparse description says the tool queries team member shifts and attendance statistics. In execution, the script prints a shift-query banner, calls only the shift listing command, and reports '未查到排班信息' when no results are returned, which contradicts the broader claim of also providing attendance statistics.

Static analysis

No suspicious patterns detected.