Back to skill

Security audit

dingtalk-feishu-sync-schedule

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent calendar-sync purpose, but it stores credentials locally and can repeatedly delete or replace calendar events without strong safeguards.

Install only if you are comfortable granting the skill DingTalk calendar read access and Feishu calendar read/write/delete access. Before scheduling it, restrict permissions on ~/.dingtalk and ~/.feishu files, avoid running it as root, remove token fragments and detailed calendar data from logs, and test manually with a backup calendar because manual Feishu events with matching title and time may be deleted or replaced.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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 (5)

T06 · System Persistence

Error
Location
SKILL.md:152
Finding

Recurring Calendar Synchronization Creates Cross-Session System Persistence

Content
View full analysis
> /var/log/dingtalk_sync.log 2>&1 ``` ### Technical Analysis The documented configuration establishes a cron task that executes three times every day and survives the original Skill session. Scheduled synchronization is related to the declared functionality and is disclosed to the user, but it is not necessary for one-time or manually initiated synchronization. The scheduled process repeatedly receives access to locally stored DingTalk application credentials, Feishu application credentials and tokens, calendar contents, and Feishu event creation and deletion capabilities. The use of `/root` paths and a system log also suggests execution in a highly privileged account, although the documentation does not explicitly require that cron itself run as root. ### Attack Path 1. A user follows the scheduling instructions and edits their crontab. 2. The synchronization script is registered to run at 09:00, 12:00, and 15:00 every day. 3. The task persists beyond the setup session and runs without renewed user approval. 4. Each execution reads stored credentials, retrieves private calendar data, refreshes tokens, deletes selected Feishu events, and creates replacement events. 5. Any existing defect or later unauthorized modification to the script is consequently executed on every schedule. ### Impact Assessment The persistent task can repeatedly exercise all privileges granted to the DingTalk and Feishu applications, including reading private schedules and modifying the configured Feishu calendar. If configured under a privileged account, compromise of the script or its writable dependencies could also result in rec ...[truncated 57 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/init_config.py:37
Finding

Sensitive Credential Files Are Written Without Enforced Restrictive Permissions

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
sync_week_ahead.py:169
Finding

Manual Feishu Events Can Be Deleted Through Ambiguous Ownership Matching

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
utils/refresh_token.py:63
Finding

Feishu Access-Token Prefix Is Disclosed in Console and Log Output

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
sync_week_ahead.py:238
Finding

Private Calendar Details Are Persistently Written to a System Log

Content
View full analysis
> /var/log/dingtalk_sync.log 2>&1 ``` ```python print(f"✅ 找到 {len(events)} 个本周日程:\n") for ev in events: status_icon = "❌" if ev.get('status') == 'cancelled' else "📅" print(f" {status_icon} {ev['summary']}: {ev['start']} ~ {ev['end']} [{ev.get('status', 'confirmed')}]") if ev['location']: print(f" 地点: {ev['location']}") print() success, deleted, total = sync_to_feishu(events) print(f"\n🎉 同步完成!删除了 {deleted} 个旧日程,创建了 {success}/{total} 个新日程") print(f"\n📅 本周同步结果({now.strftime('%m-%d')} ~ {week_end.strftime('%m-%d')}):\n") for i, ev in enumerate(sorted(events, key=lambda x: x['start_ts']), 1): print(f" {i}. {ev['summary']} | {ev['start']} ~ {ev['end']}") if ev['location']: print(f" 📍 {ev['location']}") print(f" 状态: {ev.get('status', 'confirmed')}") ``` ### Technical Analysis The synchronization script prints event titles, dates, times, locations, and status information. The recommended cron entry redirects all standard output and error output to `/var/log/dingtalk_sync.log`, causing these personal calendar details to persist outside the calendar services. No restrictive log mode, rotation policy, retention limit, or redaction mechanism is documented. This also conflicts with the documentation's assertion that synchronization logs do not contain sensitive information. ### Attack Path 1. The scheduled synchronization retrieves private DingTalk events. 2. The script prints each event's title, time, location, and status. 3. Cron appends the output to `/var/log/dingtalk_sync.log`. 4. The information accumulates over repeated executions. 5. A local user, bac ...[truncated 454 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (27)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · utils/refresh_token.py (reported line 4)May include surrounding context.

python
#!/usr/bin/env python3
"""
飞书token自动刷新工具
从 ~/.feishu/config.json 读取应用凭证和token,自动刷新 access token
"""

import os

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · utils/refresh_token.py (reported line 17)May include surrounding context.

python
#!/usr/bin/env python3
"""
飞书token自动刷新工具
从 ~/.feishu/config.json 读取应用凭证和token,自动刷新 access token
"""

import os

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% confidence
Finding

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.

Content

Scanner excerpt · SKILL.md (reported line 152)May include surrounding context.

查看

bash
crontab -l | grep dingtalk

编辑

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% confidence
Finding

The skill instructs users to configure a persistent cron job that runs automatically multiple times per day, creating durable execution on the host. Although this appears operational rather than malicious, persistence mechanisms increase risk because they continue processing tokens and calendar data unattended and may survive beyond the user's active awareness.

Content

Scanner excerpt · SKILL.md (reported line 157)May include surrounding context.

编辑

bash
crontab -e

当前配置:

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The module docstrings and function descriptions are entirely in Chinese, with no indication that language selection is optional or that the skill is intentionally limited to a Chinese-speaking context. This can violate language/locale policy when a skill implicitly forces a specific language without user opt-in.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The module documentation states '飞书配置:~/.openclaw/openclaw.json(仅读取,缺失提示用户)', which describes Feishu handling as read-only from OpenClaw with user prompting on missing values. However, later code materializes that data and writes it to ~/.feishu/config.json, creating or overwriting a local Feishu configuration file.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
76% confidence
Finding

This finding is effectively the same credential-bearing outbound request to DingTalk's token endpoint. Although the destination is fixed and uses HTTPS, sending app credentials off-host is security-sensitive and should be treated as a real exposure point rather than a harmless pattern.

Content

Scanner excerpt · scripts/init_config.py (reported line 46)May include surrounding context.

python
def get_dingtalk_token():
    """获取钉钉 access_token"""
    cfg = load_dingtalk_config()
    resp = requests.post(
        'https://api.dingtalk.com/v1.0/oauth2/accessToken',
        json={'appKey': cfg['app_key'], 'appSecret': cfg['app_secret']},
        timeout=10

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

This finding is effectively the same credential-bearing outbound request to DingTalk's token endpoint. Although the destination is fixed and uses HTTPS, sending app credentials off-host is security-sensitive and should be treated as a real exposure point rather than a harmless pattern.

Content

Scanner excerpt · scripts/init_config.py (reported line 46)May include surrounding context.

python
def get_dingtalk_token():
    """获取钉钉 access_token"""
    cfg = load_dingtalk_config()
    resp = requests.post(
        'https://api.dingtalk.com/v1.0/oauth2/accessToken',
        json={'appKey': cfg['app_key'], 'appSecret': cfg['app_secret']},
        timeout=10

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

The script sends a user's mobile number and access token to an external DingTalk API. This is expected functionality, but it is still a real privacy/security-sensitive transmission because personally identifiable information and authentication material leave the local machine.

Content

Scanner excerpt · scripts/init_config.py (reported line 62)May include surrounding context.

python
return None

    # Step1: 通过手机号获取 userid
    resp = requests.post(
        f'https://oapi.dingtalk.com/topapi/v2/user/getbymobile?access_token={token}',
        json={'mobile': mobile},
        timeout=10

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

This request sends an access token and userid to an external API to obtain a unionid. In context this is intended behavior, but it is still security-relevant because it transmits identifiers and authentication context externally, and the token is embedded in the query string where it may be more easily logged by intermediaries or tooling.

Content

Scanner excerpt · scripts/init_config.py (reported line 78)May include surrounding context.

python
return None

    # Step2: 通过 userid 获取 unionid
    resp = requests.post(
        f'https://oapi.dingtalk.com/topapi/v2/user/get?access_token={token}',
        json={'language': 'zh_CN', 'userid': userid},
        timeout=10

Tainted flow: 'userid' from requests.post (line 72, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/init_config.py (reported line 78)May include surrounding context.

python
return None

    # Step2: 通过 userid 获取 unionid
    resp = requests.post(
        f'https://oapi.dingtalk.com/topapi/v2/user/get?access_token={token}',
        json={'language': 'zh_CN', 'userid': userid},
        timeout=10

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This code performs an external notification action by invoking a messaging CLI to send content to Feishu, but the script provides no confirmation prompt, user-facing log, or explicit warning at the point of transmission. Although comments describe the behavior for readers of the source, the script itself gives no runtime disclosure before sending data derived from local logs.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 94)May include surrounding context.

md
union_id = ding_config.get('user_id')

    resp = _session.post(
        "https://api.dingtalk.com/v1.0/oauth2/accessToken",
        json={"appKey": app_key, "appSecret": app_secret},
        timeout=10
    )

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 202)May include surrounding context.

md
union_id = ding_config.get('user_id')

    resp = _session.post(
        "https://api.dingtalk.com/v1.0/oauth2/accessToken",
        json={"appKey": app_key, "appSecret": app_secret},
        timeout=10
    )

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 203)May include surrounding context.

md
union_id = ding_config.get('user_id')

    resp = _session.post(
        "https://api.dingtalk.com/v1.0/oauth2/accessToken",
        json={"appKey": app_key, "appSecret": app_secret},
        timeout=10
    )

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/init_config.py (reported line 47)May include surrounding context.

python
union_id = ding_config.get('user_id')

    resp = _session.post(
        "https://api.dingtalk.com/v1.0/oauth2/accessToken",
        json={"appKey": app_key, "appSecret": app_secret},
        timeout=10
    )

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · sync_week_ahead.py (reported line 32)May include surrounding context.

python
union_id = ding_config.get('user_id')

    resp = _session.post(
        "https://api.dingtalk.com/v1.0/oauth2/accessToken",
        json={"appKey": app_key, "appSecret": app_secret},
        timeout=10
    )

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · sync_week_ahead.py (reported line 47)May include surrounding context.

python
union_id = ding_config.get('user_id')

    resp = _session.post(
        "https://api.dingtalk.com/v1.0/oauth2/accessToken",
        json={"appKey": app_key, "appSecret": app_secret},
        timeout=10
    )

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script enumerates existing Feishu calendar events for the target time window and deletes entries that either contain the sync marker or match a title/start-time key before recreating them. This is destructive synchronization behavior, and without an explicit user confirmation or dry-run mode it can remove legitimate events or lose metadata if matching logic is imperfect or API responses are unexpected.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · utils/refresh_token.py (reported line 44)May include surrounding context.

python
return False

    url = "https://open.feishu.cn/open-apis/authen/v1/refresh_access_token"
    resp = requests.post(url, json={
        "grant_type": "refresh_token",
        "app_id": app_id,
        "app_secret": app_secret,

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The instructions hard-code "language": "zh_CN" in the DingTalk API request. This is a natural-language locale choice presented as fixed behavior, and the document does not say the user can choose another locale or why Chinese is required for this skill.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
74% confidence
Finding

The module docstring states the loader depends on ~/.dingtalk/config.json and ~/.feishu/config.json, suggesting both configs are actually consumed. In practice, the DingTalk helper only returns a path string and never reads the file, creating a documentation-to-code inconsistency about what the module does for DingTalk configuration.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The docstring for load_config says it loads the main configuration, but the implementation unconditionally returns an empty dictionary and does not load anything. This is an active mismatch between the documented intent and actual behavior, even though the parenthetical note partially explains that real loading happens elsewhere.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This code reads ~/.feishu/config.json, which may contain sensitive user or credential data, but there is no visible user-facing warning, logging, or confirmation around that access in this file. For code files, access to sensitive configuration sources should include some form of disclosure unless clearly communicated elsewhere.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script hard-codes language: 'zh_CN' when requesting DingTalk user data. This is a natural-language/locale policy concern because the file provides no opt-in, fallback, or explanation that the skill is intentionally region-specific.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.