Back to skill

Security audit

cyber-im-avatar — 赛博办公 IM 分身

Security checks for vulnerabilities and agentic risk

Overview

This chat assistant needs review because it can misstate new account permissions and recommends unrestricted file access.

Keep draft approval enabled and select conversations explicitly. Have the publisher fix permission checks when existing authorization cannot be determined, and inspect the actual provider consent screen rather than trusting the card's 'no new permissions' claim. Leave unrestricted file access disabled and use narrowly scoped file permissions. Preview mode can write settings, and the deletion command leaves daily summaries behind. The separate DSH plugin and external CLIs remain outside this artifact review.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (38)

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 · CHANGELOG.md (reported line 258)May include surrounding context.

md
新会话即落在该客服会话绑定的工作区目录里(DSH 侧边栏点开工作区就能看见、能接着聊);
  新 session id 自动写回 `chats.dsh_session`,两个入口(轮询预写 / 面板点起草)都生效。
- **起草不再自管模型配置**:dsh 引擎下插件不再解析 DSH 的 `cordis.patch.yml`、
  不再读 `~/.dsh/.credentials.yaml`、不再往子进程注入 key —— 模型/网关/凭据全部
  交给 DSH 自己的配置解析。`panel.json` 的 `llm.base_url/model/api_key` 收窄为
  **直连兜底的显式覆盖**(要用才填,填了就以它为准);「体检」里的模型标签改为
  「跟随 DSH 配置」。

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 · CHANGELOG.md (reported line 376)May include surrounding context.

md
会话落在客服会话绑定的工作区目录下,在 DSH 侧边栏可见、可点开接着聊;同一通 IM 复用同一个
  `session-…`,历史累积。会话 id 存在 `chats.dsh_session`,客服页可一键复制。
- **模型配置只留一份**:新增 `service/dshconf.py`,从 `~/.dsh/profiles/<p>/cordis.patch.yml`
  的 `llm-deepseek` / `agent-default-model` 与 `~/.dsh/.credentials.yaml` 的 refs 解析网关、模型、key;
  本机 `panel.json` 里重复的那份 base_url / api_key 已清空(备份在 `.bak-*`)。
  「DSH 里能对话」= 「值守能起草」,面板设置页只在想临时覆盖时才需要填。
- **接管路径对齐 DSH 工作区**:`/api/chats` 给每条会话带上 `workspace_title / workspace_in_dsh`;

Description-Behavior Mismatch

High
Category
MCP Tool Poisoning
Confidence
86% confidence
Finding

Searching for reauthorization messages plausibly supports the declared troubleshooting functionality. However, deleting messages as a bot is a distinct destructive capability not disclosed by the description. The approval comment limits its intended use but does not make that capability part of the declared purpose.

Content

No source excerpt is available for this finding.

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 · references/troubleshooting.md (reported line 130)May include surrounding context.

md
| 平台 | 查状态 | 判定 |
|---|---|---|
| 飞书 | `lark-cli auth status` | 看 `identities.user.tokenStatus`;`expiresAt` 是 access token(约 2 小时),`refreshExpiresAt` 是刷新凭证(约 7 天)。**refresh 也过了就必须重新扫码** |
| 企业微信 | `wecom-cli auth show` | 文本 `Status: authorized` + `Bot ID: ...`;其它状态即未授权 |
| 钉钉 | `dws auth status` | JSON `authenticated: true/false`,`message` 直接写「未登录」。**false 有两种**:真没登过;登录被组织闸门拒(见 B.2 末),后者只能从登录输出认出来 |

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 · scripts/reauth.py (reported line 456)May include surrounding context.

python
def dingtalk(args):
    rc, obj, raw = run(["dws", "auth", "status"], timeout=25)
    if isinstance(obj, dict) and obj.get("authenticated"):
        print("[ok] 钉钉已登录(access token 由 dws 自动刷新),无需重新授权")
        print("     若确实失效(如换机/撤销授权):dws auth login --device")
        return 0
    print("[scope] 该平台无 OAuth scope 概念:权限在钉钉开放平台的应用权限点里勾选,"

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/run_tests.py (reported line 60)May include surrounding context.

python
"dingtalk": "dingtalk_listen_cli.py",
        "feishu": "feishu_listen_cli.py",
    }[platform])
    env = dict(os.environ,
               IM_STANDIN_DIR=d, MOCK_SHAPE=shape, MOCK_DATA=data,
               MOCK_SELF=json.load(open(os.path.join(d, "env.json")))["platforms"][platform]["self_id"])
    return subprocess.run([PY, script] + list(args),

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/run_tests.py (reported line 267)May include surrounding context.

python
"platforms": {k: {"self_id": f"self_{k}"}
                                 for k in ("feishu", "wecom", "dingtalk")},
                   "targets": {}, "overrides": {}}, f)
    env = dict(os.environ, IM_STANDIN_DIR=d,
               PATH=bindir + os.pathsep + os.environ.get("PATH", ""))
    r = subprocess.run([PY, os.path.join(SCRIPTS, "doctor.py"), "--json"],
                       capture_output=True, text=True, env=env, timeout=120)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/run_tests.py (reported line 435)May include surrounding context.

python
"""画像超限整理:去重、历史结论 ≤20、旧结论进 history、dry-run 不落盘。"""
    print("\n[画像] 超限整理 / 去重 / 归档")
    d = tempfile.mkdtemp(prefix="standin_compact_")
    env = dict(os.environ, IM_STANDIN_DIR=d)
    script = os.path.join(SCRIPTS, "bootstrap.py")
    r = subprocess.run([PY, script, "--alias", "feishu", "测试对象", "oc_demo"],
                       capture_output=True, text=True, env=env, timeout=30)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/run_tests.py (reported line 472)May include surrounding context.

python
import stat
    print("\n[隐私] 文件权限 / 语料脱敏 / 一键删除")
    d = tempfile.mkdtemp(prefix="standin_privacy_")
    env = dict(os.environ, IM_STANDIN_DIR=d)
    script = os.path.join(SCRIPTS, "bootstrap.py")
    r = subprocess.run([PY, script, "--alias", "feishu", "测试", "oc_test"],
                       capture_output=True, text=True, env=env, timeout=30)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/run_tests.py (reported line 527)May include surrounding context.

python
d = tempfile.mkdtemp(prefix="standin_no_cli_")
    bindir = os.path.join(d, "bin")
    os.makedirs(bindir)
    env = dict(os.environ, IM_STANDIN_DIR=d, PATH=bindir)
    r = subprocess.run([PY, os.path.join(SCRIPTS, "doctor.py"), "--json"],
                       capture_output=True, text=True, env=env, timeout=60)
    try:

Skill declares no tool scope ('permissions' or 'allowed-tools') but code capabilities were detected: env, file_read, file_write, shell.

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The missing tool scope accompanies a concrete least-privilege weakness: profile maintenance relies on optional full filesystem access rather than narrowly scoped access to the required data files. Because the agent processes third-party messages and attachments, enabling that mode increases the consequences of prompt injection; textual restrictions are not an enforceable capability boundary, although default-disabled full access reduces exposure.

Content

No source excerpt is available for this finding.

File System Enumeration

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code scans file system directories looking for sensitive files. This could be reconnaissance for credential theft.

Content

Scanner excerpt · references/troubleshooting.md (reported line 448)May include surrounding context.

md
| 画像超过 12KB / 写不进 | 压缩「历史结论」的旧条目;面板写入会拒,DSH 壳内 agent 直接改文件 |
| 画像写不进去 / `FS_SANDBOX_DENIED` | 设置页开启「完整文件访问」(`dsh.full_access=true`);默认 `workspace-write` 只允许写会话工作区 |
| 面板刚改过画像,agent 又覆盖了 | DSH 壳内没有 mtime CAS;agent 写前重读、增量合并,冲突以磁盘最新内容为准 |
| 画像路径/权限不对 | `ls -l ~/.im-standin/profiles/<platform>/`;目录 700、文件 600;路径以 `env.json` 的 `profile_file` 为准 |
| 状态文件权限错 | `chmod -R u+rw ~/.im-standin`;确认不是 root 建的 |
| 想多账号/测试隔离 | `IM_STANDIN_DIR=/tmp/imtest python3 scripts/doctor.py` |
| 升级后行为诡异 | 先 `python3 tests/run_tests.py` 判断是引擎还是配置问题 |

Tainted flow: 'dest' from os.environ.get (line 133, credential/environment) → open (file write)

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/bootstrap.py (reported line 135)May include surrounding context.

python
return ""
    dest = _profile_history_path(path)
    ensure_private_dir(os.path.dirname(dest))
    with open(dest, "a", encoding="utf-8") as f:
        f.write("\n\n<!-- archived %s -->\n" % time.strftime("%Y-%m-%dT%H:%M:%S"))
        for bullet in bullets:
            f.write(bullet + "\n")

The documented no-write dry run modifies personal configuration and can create a profile.

Medium
Category
Excessive Agency
Confidence
99% confidence
Finding

The dry-run branch is reached only after ensure_profile() and save_env(), which can create a profile, modify configuration, and invoke permission updates through secure_tree(). This violates the advertised no-write preview boundary and can unexpectedly alter personal application state, although it does not itself demonstrate credential exposure or privilege escalation.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/bootstrap.py (reported line 346)May include surrounding context.

python
def run(cli, args, timeout=20):
    try:
        p = subprocess.run([cli] + args, capture_output=True, text=True, timeout=timeout)
    except Exception as e:
        return 127, None, str(e)
    raw = (p.stdout or "").strip()

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/bootstrap.py (reported line 413)May include surrounding context.

python
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                          cfg["listen_script"])
    # 用 --timeout 1 + 最小 interval 跑一轮:退出 3(超时) 即通路正常;2 才是故障
    p = subprocess.run([sys.executable, script, "--chat-id", chat_id,
                        "--interval", "2", "--timeout", "3"],
                       capture_output=True, text=True, timeout=30)
    if p.returncode == 3:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/bootstrap.py (reported line 649)May include surrounding context.

python
cfg = PLATFORMS[platform]
    script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                          cfg["listen_script"])
    p = subprocess.run([sys.executable, script, "--chat-id", chat_id,
                        "--interval", "2", "--timeout", "3"],
                       capture_output=True, text=True, timeout=60)
    if p.returncode == 3:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/doctor.py (reported line 53)May include surrounding context.

python
# ---------------------------------------------------------------- 基础工具
def run(argv, timeout=30):
    try:
        p = subprocess.run(argv, capture_output=True, text=True, timeout=timeout)
    except FileNotFoundError:
        return 127, "", f"not found: {argv[0]}"
    except subprocess.TimeoutExpired:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/doctor.py (reported line 346)May include surrounding context.

python
"--chat-id" if chat_id else "--user-id", target,
            "--interval", "2", "--timeout", "3"]
    try:
        p = subprocess.run(argv, capture_output=True, text=True, timeout=90)
    except subprocess.TimeoutExpired:
        rep.add("通路", "fail", "自检超时(>90s)",
                fix="检查网络/CLI 卡死;单独跑一次该 CLI 命令看耗时", ref="d")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/listen_core.py (reported line 81)May include surrounding context.

python
# ---- CLI 调用 -------------------------------------------------------
    def run_cli(self, args, timeout=30):
        try:
            p = subprocess.run([self.ad["cli"]] + list(args),
                               capture_output=True, text=True, timeout=timeout)
        except FileNotFoundError:
            return 127, None, f"CLI not found: {self.ad['cli']}(未安装或不在 PATH)"

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Content

Scanner excerpt · scripts/listen_core.py (reported line 234)May include surrounding context.

python
}


def resolve_deadline(until="", timeout=0):
    if until and timeout > 0:
        raise ValueError("--until 与 --timeout 不能同时使用")
    if not until:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/reauth.py (reported line 92)May include surrounding context.

python
def run(argv, timeout=60, stdin=None):
    try:
        p = subprocess.run(argv, capture_output=True, text=True,
                           timeout=timeout, input=stdin)
    except FileNotFoundError:
        return 127, None, f"not found: {argv[0]}"

The documented explicit-consent gate for added permissions is bypassed when no prior scopes are available.

Medium
Category
Excessive Agency
Confidence
98% confidence
Finding

When the prior scope set is empty, the code sets added permissions to an empty list regardless of the requested scopes, so guard_scopes permits requests without --allow-add. Because scope lookup failures also produce an empty list, unavailable authorization state can silently disable the local expansion guard and understate changes; the requested permissions include reading messages and sending as the user, although provider-side OAuth consent still applies.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · tests/run_tests.py (reported line 63)May include surrounding context.

python
env = dict(os.environ,
               IM_STANDIN_DIR=d, MOCK_SHAPE=shape, MOCK_DATA=data,
               MOCK_SELF=json.load(open(os.path.join(d, "env.json")))["platforms"][platform]["self_id"])
    return subprocess.run([PY, script] + list(args),
                          capture_output=True, text=True, env=env, timeout=60)

Tainted flow: 'env' from open (line 60, file read) → subprocess.run (code execution)

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 · tests/run_tests.py (reported line 63)May include surrounding context.

python
env = dict(os.environ,
               IM_STANDIN_DIR=d, MOCK_SHAPE=shape, MOCK_DATA=data,
               MOCK_SELF=json.load(open(os.path.join(d, "env.json")))["platforms"][platform]["self_id"])
    return subprocess.run([PY, script] + list(args),
                          capture_output=True, text=True, env=env, timeout=60)