Back to skill

Security audit

日程协办虾

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches Feishu calendar automation, but its fallback script stores access tokens insecurely and can change or delete calendar data with weak safeguards.

Review before installing. Prefer the official/plugin tool path with user OAuth over the fallback shell script. If using the script, fix token storage first, restrict Feishu app scopes, avoid shared machines, and require explicit confirmation before any create, update, attendee-notification, subscription, or delete action.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/feishu-calendar.sh:24
Finding
Tenant access token stored in an insecure predictable temporary file<![CDATA[ ## Vulnerability Details **File Location**: `scripts/feishu-calendar.sh`, lines 24-51 **Vulnerability Type**: Predictable temporary file and plaintext bearer-token storage **Risk Level**: High ### Vulnerable Code ```bash TOKEN_CACHE="/tmp/feishu_token_cache" get_token() { if [[ -f "$TOKEN_CACHE" ]]; then cached=$(cat "$TOKEN_CACHE") expire=$(echo "$cached" | jq -r '.expire // 0') now=$(date +%s) if (( now < expire )); then echo "$cached" | jq -r '.token' return fi fi local resp resp=$(curl -s -X POST "$BASE_URL/auth/v3/tenant_access_token/internal" \ -H "Content-Type: application/json" \ -d "{\"app_id\":\"${FEISHU_APP_ID}\",\"app_secret\":\"${FEISHU_APP_SECRET}\"}") local token expire_at token=$(echo "$resp" | jq -r '.tenant_access_token // empty') if [[ -z "$token" ]]; then echo "ERROR: Failed to get token: $resp" >&2 exit 1 fi expire_at=$(( $(date +%s) + 7000 )) echo "{\"token\":\"$token\",\"expire\":$expire_at}" > "$TOKEN_CACHE" echo "$token" } ``` ### Technical Analysis The script stores a Feishu tenant bearer token in the fixed path `/tmp/feishu_token_cache`. The shared temporary directory is accessible to multiple local users, and the script does not: - Set a restrictive `umask`. - Explicitly create the cache with mode `0600`. - Verify that the cache is owned by the current user. - Reject symbolic links or other unexpected file types. - Separate cache files by user, application, or tenant. - Create or replace the cache atomically. With a common `umask` of `022`, a newly created cache can be readable by other local users. Because the path is predictable, an attacker can monitor it and retrieve the bearer token while it remains valid. On systems without effective temporary-directory symlink protections, pre-creating the path as a symbolic link may also redirect the write to another file writable by the victim process. The script also trusts any unexpired JSON object al ...[truncated 1777 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store the cache in a private runtime directory rather than directly under `/tmp`, for example: - `${XDG_RUNTIME_DIR}/feishu-calendar/`, after validating its ownership and permissions. - A directory created with `mktemp -d` and mode `0700`. 2. Set `umask 077` before creating any credential-bearing file. 3. Create the token cache atomically with mode `0600`, then rename it into place. 4. Verify that the cache: - Is a regular file. - Is not a symbolic link. - Is owned by the effective user. - Is not readable or writable by group or other users. 5. Use separate cache names keyed by the current user and a non-secret application or tenant identifier. 6. Delete expired cache files and clear the cache when authentication fails. 7. Prefer an operating-system credential store or in-memory caching when available. 8. Avoid printing the token through the public `token` command unless token disclosure is an explicitly required administrative operation. A hardened implementation should use a private directory and atomic creation, for example: ```bash umask 077 CACHE_DIR="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/feishu-calendar-${UID}" mkdir -p "$CACHE_DIR" chmod 700 "$CACHE_DIR" TOKEN_CACHE="$CACHE_DIR/token-cache" tmp_cache=$(mktemp "$CACHE_DIR/token.XXXXXX") chmod 600 "$tmp_cache" jq -n --arg token "$token" --argjson expire "$expire_at" \ '{token: $token, expire: $expire}' > "$tmp_cache" mv -f "$tmp_cache" "$TOKEN_CACHE" ``` Ownership and file-type checks should still be performed before reading an existing cache. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/feishu-calendar.sh:62
Finding
Caller-controlled values are inserted into JSON request bodies without safe encoding<![CDATA[ ## Vulnerability Details **File Location**: `scripts/feishu-calendar.sh`, lines 62-112 **Vulnerability Type**: JSON injection and insufficient input validation **Risk Level**: Medium ### Vulnerable Code ```bash user_id="$2"; time_min="$3"; time_max="$4" token=$(get_token) curl -s -X POST "$BASE_URL/calendar/v4/freebusy/list?user_id_type=open_id" \ -H "Authorization: Bearer $token" \ -H "Content-Type: application/json" \ -d "{\"user_id\":\"$user_id\",\"time_min\":\"$time_min\",\"time_max\":\"$time_max\",\"only_busy\":false}" ``` ```bash user_ids_file="$2"; time_min="$3"; time_max="$4" user_ids=$(cat "$user_ids_file") token=$(get_token) curl -s -X POST "$BASE_URL/calendar/v4/freebusy/batch?user_id_type=open_id" \ -H "Authorization: Bearer $token" \ -H "Content-Type: application/json" \ -d "{\"user_ids\":$user_ids,\"time_min\":\"$time_min\",\"time_max\":\"$time_max\",\"only_busy\":false}" ``` ```bash cal_id="$2"; summary="$3"; start_ts="$4"; end_ts="$5"; desc="${6:-}" token=$(get_token) local_body="{\"summary\":\"$summary\",\"need_notification\":true,\"start_time\":{\"timestamp\":\"$start_ts\"},\"end_time\":{\"timestamp\":\"$end_ts\"},\"color\":-1,\"visibility\":0,\"reminders\":[{\"minutes\":15}]}" if [[ -n "$desc" ]]; then local_body=$(echo "$local_body" | jq --arg d "$desc" '. + {description: $d}') fi ``` ```bash cal_id="$2"; event_id="$3"; attendees_file="$4" attendees=$(cat "$attendees_file") token=$(get_token) curl -s -X POST "$BASE_URL/calendar/v4/calendars/$cal_id/events/$event_id/attendees?user_id_type=open_id" \ -H "Authorization: Bearer $token" \ -H "Content-Type: application/json" \ -d "{\"attendees\":$attendees,\"need_notification\":true}" ``` ### Technical Analysis The script constructs JSON by directly interpolating command-line arguments and file contents into quoted strings. JSON-sensitive characters such as quotation marks, backslashes, control characters, or crafted object fragments are not escaped. For examp ...[truncated 2463 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Construct every request body with `jq -n` rather than string concatenation. 2. Pass string values with `--arg` and prevalidated JSON values with `--argjson`. 3. Validate all inputs before sending a request: - Require Feishu open IDs to match the expected identifier format. - Require timestamps to contain decimal seconds only where Unix timestamps are expected. - Parse RFC 3339 values with a strict date parser. - Require calendar and event IDs to match documented formats. 4. Validate JSON files with `jq -e` and enforce an exact schema: - Root value must be an array. - Each attendee must be an object with only expected keys. - `type` must be an allowed value. - `user_id` must be a valid open ID. - Reject unknown or nested fields. 5. Enforce documented attendee and batch-size limits. 6. Use `curl --fail-with-body --show-error` and check both HTTP status and Feishu's JSON error code. 7. URL-encode query parameters with `curl --get --data-urlencode` rather than concatenating them into URLs. A safe event body can be generated as follows: ```bash if [[ ! "$start_ts" =~ ^[0-9]+$ || ! "$end_ts" =~ ^[0-9]+$ ]]; then echo "Invalid timestamp" >&2 exit 1 fi local_body=$(jq -n \ --arg summary "$summary" \ --arg description "$desc" \ --arg start "$start_ts" \ --arg end "$end_ts" \ '{ summary: $summary, description: $description, need_notification: true, start_time: {timestamp: $start}, end_time: {timestamp: $end}, color: -1, visibility: "default", reminders: [{minutes: 15}] }') ``` Attendee input should be validated before use: ```bash attendees=$(jq -e ' if type != "array" then error("attendees must be an array") else . end | map( if type != "object" or (.type != "user") or (.user_id | type != "string") or (.user_id | test("^ou_[A-Za-z0-9]+$") | not) then error("invalid attendee") else {type: .type, user_id: .use ...[truncated 153 chars]
Vulnerability Patterns
  • 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 Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The skill description advertises calendar sync, Feishu Docs/meeting-note preparation, and specific collaboration behavior, but the documented behavior does not implement several claimed capabilities while adding undeclared deletion capability. This mismatch can mislead users and policy systems about what data and actions the skill actually performs, increasing the chance of overbroad trust or unintended destructive operations.

Vague Triggers

High
Confidence
97% confidence
Finding
The activation scenarios are extremely broad and cover many common natural-language phrases, making accidental invocation more likely. Because the skill can create, modify, invite, and delete calendar events using user or app identity, over-triggering can result in unintended external actions and data access.

Credential Access

High
Category
Privilege Escalation
Content
#!/usr/bin/env bash
# feishu-calendar.sh — 飞书日历 CLI 工具
# 用法:
#   ./scripts/feishu-calendar.sh token                    # 获取 access token
#   ./scripts/feishu-calendar.sh freebusy <user_id> <start> <end>  # 查询空闲(单用户)
#   ./scripts/feishu-calendar.sh freebusy-batch <user_ids_json> <start> <end>  # 批量查询
#   ./scripts/feishu-calendar.sh primary-calendar          # 获取主日历 ID
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
#!/usr/bin/env bash
# feishu-calendar.sh — 飞书日历 CLI 工具
# 用法:
#   ./scripts/feishu-calendar.sh token                    # 获取 access token
#   ./scripts/feishu-calendar.sh freebusy <user_id> <start> <end>  # 查询空闲(单用户)
#   ./scripts/feishu-calendar.sh freebusy-batch <user_ids_json> <start> <end>  # 批量查询
#   ./scripts/feishu-calendar.sh primary-calendar          # 获取主日历 ID
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill instructs fallback to shell/API-script execution (`scripts/feishu-calendar.sh`, direct curl) but does not declare an explicit tool scope such as allowed tools or permissions. In an agent environment, undeclared shell capability undermines least-privilege controls and can enable broader command execution than users or orchestrators expect.

Skill Enumeration

Medium
Category
Agent Snooping
Content
> ⚠️ 插件工具使用**用户身份**(user_access_token),需用户完成 OAuth 授权。如授权未完成,工具会自动引导用户授权。

**插件工具使用要点**(详见 [feishu-calendar SKILL](~/.openclaw/extensions/openclaw-lark/skills/feishu-calendar/SKILL.md)):
- 时间格式:ISO 8601(带时区),如 `2026-03-27T14:00:00+08:00`
- `user_open_id` 必填:值取消息上下文的 SenderId(`ou_xxx`),确保发起人出现在参会人列表
- 参会人 ID 统一用 `open_id`(`ou_xxx` 格式)
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The workflow triggers are loosely defined and omit clear boundaries or exclusion rules, which increases ambiguity around when the agent should transition from interpretation to action. In a calendar-management skill, ambiguous triggers can cause unintended searches, invitations, updates, or deletions from casual user phrasing.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The skill claims calendar/docs functionality but the workflow expands into cloud drive and knowledge-base search, which broadens reachable data sources beyond the declared scope. In a meeting-preparation context, this can lead to over-collection of sensitive internal documents unrelated to the user's narrowly intended action.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
L135 规定“内部处理用 Asia/Shanghai (UTC+8),展示用北京时间,不展示 UTC”,属于固定 locale/timezone 输出策略。文件中未提供用户选择时区/展示语言的机制,也未说明该技能仅面向单一区域用户,因此可能违反语言或 locale 需用户选择或明确限定的要求。

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This reference documents powerful calendar operations—creating, updating, deleting events, adding attendees, and subscribing to change notifications—without any accompanying guidance on authorization, user confirmation, scope restrictions, or audit requirements. In the context of a broadly callable scheduling skill, this omission can lead downstream agent implementations to perform user-impacting actions on calendars without explicit consent or sufficient safeguards, enabling unintended meeting creation, event tampering, privacy exposure, or notification spam.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script caches a bearer access token in a predictable file under /tmp, which is a shared world-accessible location on many systems. Without setting restrictive permissions or using a secure temporary file, another local user or process may read or race on the token and use it to access Feishu calendar data and APIs.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  local resp
  resp=$(curl -s -X POST "$BASE_URL/auth/v3/tenant_access_token/internal" \
    -H "Content-Type: application/json" \
    -d "{\"app_id\":\"${FEISHU_APP_ID}\",\"app_secret\":\"${FEISHU_APP_SECRET}\"}")
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
if [[ $# -ne 4 ]]; then echo "用法: freebusy <user_id> <start_rfc3339> <end_rfc3339>" >&2; exit 1; fi
    user_id="$2"; time_min="$3"; time_max="$4"
    token=$(get_token)
    curl -s -X POST "$BASE_URL/calendar/v4/freebusy/list?user_id_type=open_id" \
      -H "Authorization: Bearer $token" \
      -H "Content-Type: application/json" \
      -d "{\"user_id\":\"$user_id\",\"time_min\":\"$time_min\",\"time_max\":\"$time_max\",\"only_busy\":false}"
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
if [[ -n "$desc" ]]; then
      local_body=$(echo "$local_body" | jq --arg d "$desc" '. + {description: $d}')
    fi
    curl -s -X POST "$BASE_URL/calendar/v4/calendars/$cal_id/events" \
      -H "Authorization: Bearer $token" \
      -H "Content-Type: application/json" \
      -d "$local_body"
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The delete-event command performs an irreversible calendar event deletion via HTTP DELETE, but there is no confirmation prompt, warning message, or explicit caution in the help text. While deletion is part of the command's purpose, the script does not provide any user disclosure that the action is destructive before executing it.

Description-Behavior Mismatch

Low
Confidence
81% confidence
Finding
The manifest describes calendar collaboration and document preparation using Feishu Calendar and Docs APIs, but this workflow explicitly calls `feishu_search_user` to resolve people by name. User-directory lookup is a distinct capability not mentioned in the manifest description or dependencies, so the documented behavior exceeds the stated scope.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
All user-facing comments and usage text are presented only in Chinese, with no indication of language choice or opt-in. This can violate a language/locale policy when a skill imposes a single language without explicitly limiting itself to a Chinese-only context.

Static analysis

No suspicious patterns detected.