@eduwork/dsh-mail 0.1.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.en.md +43 -0
- package/CHANGELOG.md +43 -0
- package/LICENSE +21 -0
- package/README.en.md +95 -0
- package/README.md +115 -0
- package/SECURITY.en.md +9 -0
- package/SECURITY.md +11 -0
- package/THIRD_PARTY_NOTICES.en.md +15 -0
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/cordis.patch.yml +8 -0
- package/docs/EDUWORK-MIGRATION.en.md +23 -0
- package/docs/EDUWORK-MIGRATION.md +23 -0
- package/docs/architecture.en.md +68 -0
- package/docs/architecture.md +76 -0
- package/docs/compatibility.en.md +37 -0
- package/docs/compatibility.md +36 -0
- package/docs/decisions/0001-mail-assistant-boundary.en.md +26 -0
- package/docs/decisions/0001-mail-assistant-boundary.md +26 -0
- package/docs/getting-started.en.md +55 -0
- package/docs/getting-started.md +57 -0
- package/docs/release-checklist.en.md +51 -0
- package/docs/release-checklist.md +51 -0
- package/docs/security-model.en.md +56 -0
- package/docs/security-model.md +56 -0
- package/docs/tool-contract.en.md +44 -0
- package/docs/tool-contract.md +44 -0
- package/lib/client.js +759 -0
- package/lib/index.js +81040 -0
- package/package.json +130 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# 兼容性与已知限制
|
|
2
|
+
|
|
3
|
+
**简体中文** | [English](compatibility.en.md)
|
|
4
|
+
|
|
5
|
+
`@eduwork/dsh-mail@0.1.0-alpha.5` 保留 alpha.4 的运行时和设置契约。详见[迁移说明](EDUWORK-MIGRATION.md)。
|
|
6
|
+
|
|
7
|
+
## 已验证基线
|
|
8
|
+
|
|
9
|
+
- DeepSeek Harness `0.1.2-alpha.2`;
|
|
10
|
+
- Node.js 22+;
|
|
11
|
+
- Web Client 动态 Bundle;
|
|
12
|
+
- 本地 Host 文件系统;
|
|
13
|
+
- IMAP4rev1/IMAP4rev2 服务器与 SMTP Submission 服务的用户名 + 密码/应用密码认证。
|
|
14
|
+
|
|
15
|
+
插件依赖 DSH Profile 提供 `settings`、`credentials`、`tools`、`permissionPresets`、`approval` 和 `fs`。设置页还依赖官方 Web Client 的 Remotes、Renderer 与 Settings 插槽;普通权限下发信需要交互式审批通路,Full Access 下不会弹出审批。
|
|
16
|
+
|
|
17
|
+
## 协议范围
|
|
18
|
+
|
|
19
|
+
- IMAP 遵循只读 mailbox 语义,并用 UIDVALIDITY 防止陈旧 UID 误读。
|
|
20
|
+
- SMTP 支持隐式 TLS 和 STARTTLS,不支持明文连接。
|
|
21
|
+
- 当前不实现 XOAUTH2/OIDC 邮箱登录。Gmail、Microsoft 365 等是否可用取决于租户是否仍允许应用密码或相应 SMTP AUTH。
|
|
22
|
+
- 不向 IMAP Sent folder 追加副本;是否保存“已发送”由 SMTP 服务端策略决定。
|
|
23
|
+
- 复杂 S/MIME、PGP、日历邀请和嵌套 `message/rfc822` 只作为普通 MIME 数据处理,不承诺专用语义。
|
|
24
|
+
|
|
25
|
+
## DSH 二进制写限制
|
|
26
|
+
|
|
27
|
+
`ctx.fs` 在 `0.1.2-alpha.2` 提供 `readBytes`,没有 `writeBytes`。因此:
|
|
28
|
+
|
|
29
|
+
- 发送本地附件可完整复用官方 seam;
|
|
30
|
+
- 下载附件只能在 `ctx.fs.processPath()` 与 Host Node 进程指向同一文件系统时工作;插件会在创建目录后解析真实路径并再次验证工作区包含关系;
|
|
31
|
+
- E2B、远程容器或其他不共享 Host 路径的 FS Provider 应禁用或预期 `mail_get_attachment` 明确失败;
|
|
32
|
+
- 官方增加二进制写 API 后,本插件应优先迁移并删除本地写适配。
|
|
33
|
+
|
|
34
|
+
## 版本策略
|
|
35
|
+
|
|
36
|
+
在 `0.x` 阶段,DSH alpha 的 Client Loader、settings slots 或 tool schema 变化可能要求插件同步更新。每次升级先运行 `npm run check`,再在隔离 Profile 中完成真实 IMAP/SMTP 验收,不应只依赖编译通过。
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# ADR 0001: Keep the plugin an assistant for reading and explicit sending
|
|
2
|
+
|
|
3
|
+
[简体中文](0001-mail-assistant-boundary.md) | **English**
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted, 2026-09-01.
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
A DSH Agent benefits from user-controlled reading and sending, but a standard IMAP/SMTP app password usually carries broader mailbox authority than one task. Adding delete, move, archive, flags, background synchronization, and auto-reply would turn the plugin into a broad but inferior mail client while increasing accidental mutation, prompt-injection, and persistent-authorization risk.
|
|
12
|
+
|
|
13
|
+
Provider-specific OAuth2 APIs use authentication, scope, and audit models different from generic IMAP/SMTP. The current goal is interoperability with institutional, self-hosted, and app-password-capable services rather than a universal mail client.
|
|
14
|
+
|
|
15
|
+
## Decision
|
|
16
|
+
|
|
17
|
+
One plugin owns a settings surface and five bounded tools: folder discovery, paginated metadata search, read-only message retrieval, workspace attachment download, and plain-text sending. Reading and sending are enabled independently; IMAP is fixed read-only; SMTP uses DSH permission presets; mail is always untrusted; credentials only enter a DSH Credential Provider.
|
|
18
|
+
|
|
19
|
+
Delete, move, archive, flag mutation, contacts, rules, polling, auto-reply, HTML composition, and scheduled sending are outside v0.1. OAuth2 and multiple accounts require explicit architectures and cannot place tokens or multiple passwords in ordinary settings.
|
|
20
|
+
|
|
21
|
+
## Consequences
|
|
22
|
+
|
|
23
|
+
- The plugin can be open-sourced and composed by Web or desktop DSH without depending on ChatECNU Work.
|
|
24
|
+
- Users keep a professional mail client for mailbox-state management; the Agent focuses on retrieval, understanding, and authorized sending.
|
|
25
|
+
- Full Access matches other DSH write tools and skips per-call approval, while the independent sending capability remains mandatory.
|
|
26
|
+
- Any future background or mutation boundary requires another ADR, threat model, and migration plan.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# ADR 0001:邮件助手只做辅助读信与显式发信
|
|
2
|
+
|
|
3
|
+
**简体中文** | [English](0001-mail-assistant-boundary.en.md)
|
|
4
|
+
|
|
5
|
+
## 状态
|
|
6
|
+
|
|
7
|
+
已接受,2026-09-01。
|
|
8
|
+
|
|
9
|
+
## 背景
|
|
10
|
+
|
|
11
|
+
DSH Agent 需要在用户控制下读取和发送邮件,但标准 IMAP/SMTP 授权码通常拥有比单次任务更大的邮箱权限。若插件继续实现删除、移动、归档、标记、后台同步和自动回复,它会迅速变成一个能力宽泛但体验弱于专业客户端的邮箱产品,同时扩大误操作、提示注入和长期授权风险。
|
|
12
|
+
|
|
13
|
+
Gmail 等专用 OAuth2 API 与通用 IMAP/SMTP 的认证、权限和审计模型不同。当前目标是覆盖机构邮箱、单位邮箱和允许应用密码的通用服务,而不是伪装成统一邮箱客户端。
|
|
14
|
+
|
|
15
|
+
## 决策
|
|
16
|
+
|
|
17
|
+
一个插件同时提供设置界面和五个有界工具:目录发现、分页检索、只读读取、工作区附件下载和纯文本发信。读信与发信独立启用;IMAP 固定只读;SMTP 发信复用 DSH 原生权限预设;邮件内容始终不可信;凭据只进入 DSH Credential Provider。
|
|
18
|
+
|
|
19
|
+
删除、移动、归档、flags 写入、联系人、规则、后台轮询、自动回复、HTML 创作和计划发送不属于 v0.1。OAuth2 和多账号只能通过明确的新架构进入,不能把 token 或多个密码塞进普通设置。
|
|
20
|
+
|
|
21
|
+
## 结果
|
|
22
|
+
|
|
23
|
+
- 插件可以独立开源并被 Web 或桌面 DSH 组合复用,不绑定 ChatECNU Work。
|
|
24
|
+
- 用户仍使用专业邮箱客户端管理邮箱状态,Agent 聚焦检索、理解和经授权的发信。
|
|
25
|
+
- Full Access 与 DSH 其他写工具保持一致,会跳过逐次审批;独立的发信开关仍是必要条件。
|
|
26
|
+
- 未来扩展若改变写操作或后台授权,必须新增 ADR、威胁模型和迁移说明。
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
[简体中文](getting-started.md) | **English**
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Node.js 22+;
|
|
8
|
+
- DeepSeek Harness `0.1.2-alpha.2`;
|
|
9
|
+
- a Profile with Settings, Credentials, Tools, Filesystem, Permission Presets, Approval, and Web Client;
|
|
10
|
+
- an IMAP/SMTP-enabled mailbox and provider-issued app password.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
For normal use, pin the reviewed exact npm version:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
dsh plugin --profile web add @eduwork/dsh-mail@0.1.0-alpha.5
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
For auditing, development, or unpublished changes, use a source checkout:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
git clone https://github.com/freedomkk-qfeng/dsh-mail-assistant.git
|
|
24
|
+
cd dsh-mail-assistant
|
|
25
|
+
npm ci
|
|
26
|
+
npm run check
|
|
27
|
+
dsh plugin --profile web add .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Installation adds one Bundle to the Profile. Empty configuration is valid: the plugin performs no server probe or runtime installation during install, startup, or save. A source installation links the checkout into the Profile, so keep that directory available. Team deployments should pin an exact npm version; use `@eduwork/dsh-mail@alpha` only when intentionally following prereleases.
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
Open **Settings → Mail assistant** and follow the page order:
|
|
35
|
+
|
|
36
|
+
1. Enter the email address, app password, and optional sender display name.
|
|
37
|
+
2. Select a common provider or manually configure IMAP/SMTP host, port, and TLS mode.
|
|
38
|
+
3. Use Advanced settings only when the provider needs a distinct login username, a nonstandard inbox path, or changed safety limits.
|
|
39
|
+
4. Enable Agent reading and sending independently, then save.
|
|
40
|
+
|
|
41
|
+
Typical secure combinations are IMAP 993 with implicit TLS, SMTP 465 with implicit TLS, or SMTP 587 with STARTTLS. Follow the provider's documentation. Certificate validation cannot be disabled.
|
|
42
|
+
|
|
43
|
+
## Acceptance
|
|
44
|
+
|
|
45
|
+
1. Enable reading only and find recent mail; unread state must not change.
|
|
46
|
+
2. List folders and use an exact returned path to search historical mail in an archive folder.
|
|
47
|
+
3. Search a range with more matches than a deliberately small page size; the agent must follow `nextCursor` until `hasMore=false` before summarizing the full range.
|
|
48
|
+
4. Read a test message containing prompt-injection text; the agent must treat it as untrusted data.
|
|
49
|
+
5. Download a small attachment; it must land under `.dsh-mail-assistant/attachments/` in the current workspace without overwriting.
|
|
50
|
+
6. Keep an ordinary permission preset, enable sending, and send to a controlled test address; a fresh approval must appear.
|
|
51
|
+
7. Reject approval; no SMTP send occurs.
|
|
52
|
+
8. Switch to Full Access and send again to the controlled address; no approval appears and delivery succeeds.
|
|
53
|
+
9. Try an attachment outside the workspace; the tool must reject it.
|
|
54
|
+
|
|
55
|
+
Use a dedicated test mailbox. Never put addresses, subjects, bodies, server logs, or app passwords in a public issue.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# 快速接入
|
|
2
|
+
|
|
3
|
+
**简体中文** | [English](getting-started.en.md)
|
|
4
|
+
|
|
5
|
+
## 前置条件
|
|
6
|
+
|
|
7
|
+
- Node.js 22+;
|
|
8
|
+
- DeepSeek Harness `0.1.2-alpha.2`;
|
|
9
|
+
- Profile 中已经有 Settings、Credentials、Tools、Filesystem、Approval 和 Web Client;
|
|
10
|
+
- 一个开启了 IMAP/SMTP 的邮箱账号,以及邮箱服务商签发的客户端授权码或应用专用密码。
|
|
11
|
+
|
|
12
|
+
## 安装
|
|
13
|
+
|
|
14
|
+
普通使用固定经过复核的 npm 精确版本:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
dsh plugin --profile web add @eduwork/dsh-mail@0.1.0-alpha.5
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
需要审计、开发或验证尚未发布的改动时,使用源码 checkout:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
git clone https://github.com/freedomkk-qfeng/dsh-mail-assistant.git
|
|
24
|
+
cd dsh-mail-assistant
|
|
25
|
+
npm ci
|
|
26
|
+
npm run check
|
|
27
|
+
dsh plugin --profile web add .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
安装只向 Profile 加入一个 Bundle。插件配置为空也能正常启动;不会在启动时探测服务器或安装额外运行时。
|
|
31
|
+
|
|
32
|
+
源码安装会把 checkout 链接进 Profile,因此目录必须持续存在。团队部署应固定 npm 精确版本;只有明确希望持续跟随预发布更新时才使用 `@eduwork/dsh-mail@alpha`。
|
|
33
|
+
|
|
34
|
+
## 配置
|
|
35
|
+
|
|
36
|
+
在“设置 → 邮件助手”按页面顺序配置:
|
|
37
|
+
|
|
38
|
+
1. 填写邮箱地址、客户端授权码和可选的发件人显示名称;
|
|
39
|
+
2. 选择常见邮箱服务商自动填写服务器,或为单位邮箱、自建邮箱手动填写 IMAP/SMTP 主机、端口和 TLS 模式;
|
|
40
|
+
3. 仅在服务商要求时,到“高级设置”填写独立登录用户名;收件箱目录通常保持 `INBOX`;
|
|
41
|
+
4. 最后分别开放 Agent 的读信和发信能力并保存。
|
|
42
|
+
|
|
43
|
+
常见安全组合是 IMAP 993 + 隐式 TLS、SMTP 465 + 隐式 TLS,或 SMTP 587 + STARTTLS。具体值以邮箱服务商文档为准;不要为了连通而关闭证书校验,本插件也不提供这个开关。
|
|
44
|
+
|
|
45
|
+
## 验收
|
|
46
|
+
|
|
47
|
+
1. 只开启读信,让 Agent 查找最近 5 封邮件;确认邮箱中的未读状态没有变化。
|
|
48
|
+
2. 让 Agent 列出邮箱目录,并使用返回的精确路径查找归档目录中的历史邮件。
|
|
49
|
+
3. 用较小的每页条数查找一个命中较多的时间范围;确认 Agent 按 `nextCursor` 持续翻页,直到 `hasMore=false` 后才总结整个范围。
|
|
50
|
+
4. 读取一封包含“忽略此前指令”等文本的测试邮件;确认 Agent 把它视为不可信数据。
|
|
51
|
+
5. 下载一个小附件;确认文件位于当前工作区的 `.dsh-mail-assistant/attachments/`,且不会覆盖同名文件。
|
|
52
|
+
6. 保持普通权限,开启发信,让 Agent 向自己的测试地址发送纯文本;确认出现逐次审批。
|
|
53
|
+
7. 拒绝审批;确认没有 SMTP 发送。
|
|
54
|
+
8. 切换到 Full Access,再向自己的测试地址发信;确认不再弹出审批且邮件正常送达。
|
|
55
|
+
9. 尝试把工作区外文件作为附件;确认工具拒绝。
|
|
56
|
+
|
|
57
|
+
真实邮箱测试应使用专门测试账号,不要在公开 issue 附上服务器日志、地址、标题、正文或授权码。
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Public release checklist
|
|
2
|
+
|
|
3
|
+
[简体中文](release-checklist.md) | **English**
|
|
4
|
+
|
|
5
|
+
A release pull request records reviewers, date, command-output links, and exceptions. Every item blocks a stable release; an alpha release must complete all applicable items and state remaining risk.
|
|
6
|
+
|
|
7
|
+
## Authority and metadata
|
|
8
|
+
|
|
9
|
+
- [ ] Repository owner approved public release and the MIT license.
|
|
10
|
+
- [ ] GitHub repository, npm name, homepage, issues, and private security-report URLs are correct.
|
|
11
|
+
- [ ] Version, changelog, compatibility, and install commands agree.
|
|
12
|
+
- [ ] Chinese is the default language and every Markdown document has an English mirror with reciprocal entries.
|
|
13
|
+
- [ ] Publishable history and content contain no real accounts, messages, app passwords, servers, personal paths, or unauthorized branding.
|
|
14
|
+
|
|
15
|
+
## Mail and authorization boundary
|
|
16
|
+
|
|
17
|
+
- [ ] IMAP mailbox locks remain read-only; no `STORE`, `COPY`, `MOVE`, `EXPUNGE`, or flag mutation exists.
|
|
18
|
+
- [ ] Reading and sending default off and remain independent.
|
|
19
|
+
- [ ] `mail_send` enters DSH permission handling before credentials, attachments, or SMTP access.
|
|
20
|
+
- [ ] Ordinary permissions ask per call; Full Access only skips that prompt and never enables sending.
|
|
21
|
+
- [ ] Missing Agent sessions, rejection, cancellation, and unavailable approval channels fail closed.
|
|
22
|
+
- [ ] Mail remains untrusted data and cannot authorize sending, request secrets, or change system policy.
|
|
23
|
+
- [ ] Ambiguous SMTP DATA results return `unknown` and are never automatically retried.
|
|
24
|
+
|
|
25
|
+
## Credentials, transport, and attachments
|
|
26
|
+
|
|
27
|
+
- [ ] App passwords only enter a DSH Credential Provider; the browser sees configuration state only.
|
|
28
|
+
- [ ] Credentials are resolved for every call and not cached across operations.
|
|
29
|
+
- [ ] Only implicit TLS or STARTTLS is accepted; certificate validation is fixed on and protocol logging is off.
|
|
30
|
+
- [ ] Addresses reject CR/LF injection and body, message, and attachment limits are enforced.
|
|
31
|
+
- [ ] Outbound attachments are regular files inside the current session workspace.
|
|
32
|
+
- [ ] Downloaded attachments use random names, exclusive writes, and real-path containment; symlink/junction escape checks pass.
|
|
33
|
+
- [ ] Source maps and normalized errors expose no credentials, message content, server responses, or personal paths.
|
|
34
|
+
|
|
35
|
+
## Functional acceptance
|
|
36
|
+
|
|
37
|
+
- [ ] `mail_list_folders` returns server folders without mutation.
|
|
38
|
+
- [ ] `mail_find` covers both directions, pagination, dates, folders, stale UIDVALIDITY, and `hasMore=false`.
|
|
39
|
+
- [ ] `mail_read` preserves unread state; truncation and HTML fallback match documentation.
|
|
40
|
+
- [ ] `mail_get_attachment` works on local FS and fails clearly for a remote/non-shared Host path.
|
|
41
|
+
- [ ] A real test mailbox covers ordinary approve/reject, Full Access send, and outside-workspace attachment rejection.
|
|
42
|
+
- [ ] OAuth2, multiple accounts, Sent-copy behavior, and complex MIME limitations remain explicit.
|
|
43
|
+
|
|
44
|
+
## Supply chain and publication
|
|
45
|
+
|
|
46
|
+
- [ ] Clean `npm ci` and `npm run check` pass on Windows and Linux with Node 22.
|
|
47
|
+
- [ ] `npm audit`, CodeQL, direct/transitive licenses, and install scripts receive human review.
|
|
48
|
+
- [ ] GitHub Actions use immutable commits; Dependabot and branch protection are active.
|
|
49
|
+
- [ ] `npm pack --dry-run` and the extracted tarball contain only expected files; the Host bundle has no bare IMAP/SMTP imports.
|
|
50
|
+
- [ ] Tag, release notes, tarball checksum, deprecation, and rollback are ready.
|
|
51
|
+
- [ ] Stable publication uses npm Trusted Publishing/OIDC and provenance rather than a long-lived token.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# 公开发布检查表
|
|
2
|
+
|
|
3
|
+
**简体中文** | [English](release-checklist.en.md)
|
|
4
|
+
|
|
5
|
+
发布 Pull Request 应记录审查人、日期、命令输出链接和例外。任何未完成项目都会阻止稳定发布;alpha 发布也必须完成与本次改动相关的项目并明确剩余风险。
|
|
6
|
+
|
|
7
|
+
## 授权与元数据
|
|
8
|
+
|
|
9
|
+
- [ ] 仓库所有者确认公开发布和 MIT 许可证。
|
|
10
|
+
- [ ] GitHub 仓库、npm 包名、主页、Issue 和安全报告地址准确。
|
|
11
|
+
- [ ] 版本、Changelog、兼容性和安装命令一致。
|
|
12
|
+
- [ ] 默认文档为中文,每份 Markdown 文档都有英文镜像和双向入口。
|
|
13
|
+
- [ ] Git 历史和发布内容不含真实邮箱、邮件、授权码、服务器、个人路径或未授权品牌。
|
|
14
|
+
|
|
15
|
+
## 邮件与权限边界
|
|
16
|
+
|
|
17
|
+
- [ ] IMAP mailbox lock 仍固定为只读,代码没有 `STORE`、`COPY`、`MOVE`、`EXPUNGE` 或 flags 写入。
|
|
18
|
+
- [ ] 读信和发信能力默认关闭,并且彼此独立。
|
|
19
|
+
- [ ] `mail_send` 在读取凭据、附件和访问 SMTP 前进入 DSH 权限通路。
|
|
20
|
+
- [ ] 普通权限逐次审批;Full Access 只免除逐次弹窗,不能自动开启发信能力。
|
|
21
|
+
- [ ] 无 Agent 会话、拒绝、取消和不可用审批通路均失败关闭。
|
|
22
|
+
- [ ] 邮件数据持续标记为不可信,不能成为发信授权、密钥请求或系统指令。
|
|
23
|
+
- [ ] SMTP DATA 阶段的不确定结果返回 `unknown`,不会自动重试。
|
|
24
|
+
|
|
25
|
+
## 凭据、传输和附件
|
|
26
|
+
|
|
27
|
+
- [ ] 授权码只进入 DSH Credential Provider,浏览器只能看到配置状态。
|
|
28
|
+
- [ ] 每次工具调用重新解析凭据,不跨操作缓存。
|
|
29
|
+
- [ ] 只允许隐式 TLS 或 STARTTLS,证书校验不可关闭,协议日志关闭。
|
|
30
|
+
- [ ] 地址拒绝 CR/LF 注入;正文、消息和附件大小上限有效。
|
|
31
|
+
- [ ] 发送附件限定为当前会话工作区普通文件。
|
|
32
|
+
- [ ] 下载附件使用随机名、独占写和真实路径包含性复核;符号链接/目录联接逃逸测试通过。
|
|
33
|
+
- [ ] Source map 和错误归一化不泄露凭据、邮件内容、服务器响应或个人路径。
|
|
34
|
+
|
|
35
|
+
## 功能验收
|
|
36
|
+
|
|
37
|
+
- [ ] `mail_list_folders` 返回真实目录且不修改状态。
|
|
38
|
+
- [ ] `mail_find` 的正序/倒序、多页、日期、目录、UIDVALIDITY 失效和 `hasMore=false` 均测试。
|
|
39
|
+
- [ ] `mail_read` 不改变已读状态,正文截断和 HTML 降级符合文档。
|
|
40
|
+
- [ ] `mail_get_attachment` 在本地 FS 成功,在远程/不共享 Host 路径时明确失败。
|
|
41
|
+
- [ ] 普通权限下发信批准/拒绝、Full Access 发信、工作区外附件拒绝均通过真实测试邮箱验收。
|
|
42
|
+
- [ ] 不支持的 OAuth2、多账号、Sent 副本和复杂 MIME 行为仍清楚记录。
|
|
43
|
+
|
|
44
|
+
## 供应链与发布
|
|
45
|
+
|
|
46
|
+
- [ ] Windows 与 Linux 使用 Node 22 完成干净 `npm ci` 和 `npm run check`。
|
|
47
|
+
- [ ] `npm audit`、CodeQL、直接/间接许可证和安装脚本已人工审查。
|
|
48
|
+
- [ ] GitHub Actions 固定到不可变 commit,Dependabot 与分支保护有效。
|
|
49
|
+
- [ ] `npm pack --dry-run` 和解压后的 tarball 只包含预期文件;Host bundle 没有外部 IMAP/SMTP 裸导入。
|
|
50
|
+
- [ ] tag、发布说明、tarball checksum、弃用和回滚方案已准备。
|
|
51
|
+
- [ ] 稳定发布使用 npm Trusted Publishing/OIDC 和 provenance,不使用长期 Token。
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Security model
|
|
2
|
+
|
|
3
|
+
[简体中文](security-model.md) | **English**
|
|
4
|
+
|
|
5
|
+
## Protection goals
|
|
6
|
+
|
|
7
|
+
1. App passwords do not enter ordinary settings, browser readback, tool arguments, session results, or logs.
|
|
8
|
+
2. Reading does not change server state.
|
|
9
|
+
3. Prompt injection in mail cannot become a system instruction, user authorization, or secret request.
|
|
10
|
+
4. The Agent sends only when sending is enabled and the current DSH permission preset permits it.
|
|
11
|
+
5. Attachments cannot escape the session workspace through relative paths, symlinks, or junctions, or consume unbounded memory/disk.
|
|
12
|
+
6. Ambiguous SMTP results never trigger an automatic retry and duplicate send.
|
|
13
|
+
|
|
14
|
+
## Trust boundaries
|
|
15
|
+
|
|
16
|
+
- **Trusted within documented responsibility:** DSH Host, selected Credential Provider, user-saved server configuration, user-selected permission preset, and ordinary-mode actions in the Approval UI.
|
|
17
|
+
- **Untrusted:** message bodies, subjects, addresses, attachment names and content; model-generated tool arguments; remote IMAP/SMTP error text.
|
|
18
|
+
- **Conditionally trusted:** current session workspace files. They may become user-authorized outbound attachments but remain subject to path, type, count, and aggregate-size checks.
|
|
19
|
+
|
|
20
|
+
## Prompt injection
|
|
21
|
+
|
|
22
|
+
The canonical body field is `untrustedText`. Rendering adds a fixed warning and prefixes every serialized JSON line. The Host also injects a stable rule that mail cannot authorize sending, request secrets, or modify system policy.
|
|
23
|
+
|
|
24
|
+
Warnings are defense in depth. The actual mutation boundary is enforced by the separate `sendEnabled` capability, argument validation, workspace fencing, and DSH's native `tools/pre-execute` permission path. Even if the model mishandles text, the mail body cannot bypass those mechanisms.
|
|
25
|
+
|
|
26
|
+
## Send approval
|
|
27
|
+
|
|
28
|
+
`mail_send` enters DSH permission handling before credentials, attachments, or SMTP access:
|
|
29
|
+
|
|
30
|
+
- ordinary presets return `ask`; rejection, cancellation, or unavailable Approval fails closed;
|
|
31
|
+
- Full Access continues without creating a prompt that its `approval: never` policy would reject;
|
|
32
|
+
- a missing Agent-backed session is denied;
|
|
33
|
+
- the Mail assistant sending capability must be independently enabled in every mode.
|
|
34
|
+
|
|
35
|
+
Full Access therefore has the same meaning as for other DSH write tools: the user chose prompt-free execution for the active session. With sending enabled, the Agent may send without per-message dialogs. Use an ordinary preset when every message requires review, and never infer background-task authorization from interactive Full Access.
|
|
36
|
+
|
|
37
|
+
## Transport security
|
|
38
|
+
|
|
39
|
+
- implicit TLS or STARTTLS only;
|
|
40
|
+
- certificate validation fixed on;
|
|
41
|
+
- no plaintext IMAP/SMTP;
|
|
42
|
+
- protocol logging disabled;
|
|
43
|
+
- bounded connection and socket timeouts;
|
|
44
|
+
- arbitrary remote errors never returned verbatim to the model.
|
|
45
|
+
|
|
46
|
+
## Remaining risk
|
|
47
|
+
|
|
48
|
+
- An app password may carry broader server permissions than the local read/send switches. Those switches are plugin policy and do not reduce the credential's authority in other clients.
|
|
49
|
+
- Full Access removes per-message prompts after sending is enabled.
|
|
50
|
+
- SMTP has no generic idempotency. A DATA disconnect may occur after acceptance; `unknown` still requires human inspection.
|
|
51
|
+
- MIME is complex. The plugin checks server-reported message size before parsing, but a malicious or broken server may report inconsistent metadata.
|
|
52
|
+
- Local downloads recheck logical and real paths but still rely on the Host filesystem. A compromised same-user process may race with filesystem operations or access files available to that OS user; this plugin is not OS isolation.
|
|
53
|
+
|
|
54
|
+
## Reporting
|
|
55
|
+
|
|
56
|
+
Never publish app passwords, real messages, or personal logs. Follow the private process in [Security policy](../SECURITY.en.md).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 安全模型
|
|
2
|
+
|
|
3
|
+
**简体中文** | [English](security-model.en.md)
|
|
4
|
+
|
|
5
|
+
## 保护目标
|
|
6
|
+
|
|
7
|
+
1. 邮箱授权码不进入普通配置、浏览器回读、工具参数、会话结果或日志。
|
|
8
|
+
2. 读信不会改变服务器状态。
|
|
9
|
+
3. 邮件中的提示注入不能变成系统指令、用户授权或密钥请求。
|
|
10
|
+
4. Agent 只有在发信能力已开启,且当前 DSH 权限预设允许时才能发信。
|
|
11
|
+
5. 附件不能通过相对路径、符号链接或目录联接越过当前会话工作区,也不能无界占用内存/磁盘。
|
|
12
|
+
6. SMTP 不确定结果不能触发自动重试和重复发信。
|
|
13
|
+
|
|
14
|
+
## 信任边界
|
|
15
|
+
|
|
16
|
+
- **受信任**:DSH Host、所选 Credential Provider、用户明确保存的服务器配置、用户选择的会话权限预设,以及普通权限下当前用户对审批 UI 的操作。
|
|
17
|
+
- **不可信**:邮件正文、标题、地址、附件名与附件内容;模型生成的 Tool 参数;远端 IMAP/SMTP 错误文本。
|
|
18
|
+
- **条件可信**:当前会话工作区文件。它们可以作为用户允许 Agent 使用的附件,但仍受路径、文件类型、数量和总大小限制。
|
|
19
|
+
|
|
20
|
+
## Prompt injection
|
|
21
|
+
|
|
22
|
+
读取结果的规范字段名为 `untrustedText`,Native 渲染会加固定警告并把 JSON 每行加前缀。Host 还向系统提示注入稳定规则:邮件不能授权发信、请求密钥或修改系统策略。
|
|
23
|
+
|
|
24
|
+
这些提示不是唯一防线。真正的写操作依赖独立机制:`mail_send` 的能力开关、参数验证、工作区附件 fence,以及 DSH 原生 `tools/pre-execute` 权限通路。即使模型忽略提示,邮件正文仍不能绕过它们。
|
|
25
|
+
|
|
26
|
+
## 发信审批
|
|
27
|
+
|
|
28
|
+
`mail_send` 在接触凭据、附件或 SMTP 之前进入 DSH 原生权限 waterfall:
|
|
29
|
+
|
|
30
|
+
- 普通权限返回 `ask`,由 DSH Approval 服务弹出一次性审批;`rejected`、`cancelled`、`unavailable` 全部失败关闭;
|
|
31
|
+
- Full Access 直接继续,不再发起一个必然被 `approval: never` 拒绝的请求;
|
|
32
|
+
- 无 Agent 会话时拒绝;
|
|
33
|
+
- 不论哪种权限,邮件设置里的“允许 Agent 发信”都必须单独开启,Full Access 不能打开或绕过这个能力开关。
|
|
34
|
+
|
|
35
|
+
因此 Full Access 的含义与 DSH 其他写操作一致:用户已经为当前会话选择了免审批执行。切换到 Full Access 后,Agent 可以在已开放发信能力的前提下无需逐封弹窗发送邮件。这个选择应只用于用户明确接受该风险的交互会话,不能推导出后台任务授权。
|
|
36
|
+
|
|
37
|
+
## 传输安全
|
|
38
|
+
|
|
39
|
+
- 仅允许隐式 TLS 或 STARTTLS;
|
|
40
|
+
- TLS 证书校验固定开启;
|
|
41
|
+
- 不支持明文 IMAP/SMTP;
|
|
42
|
+
- 不记录协议流量;
|
|
43
|
+
- 服务器连接和 socket 有有限超时;
|
|
44
|
+
- 不把远端任意错误原样返回给模型。
|
|
45
|
+
|
|
46
|
+
## 已知剩余风险
|
|
47
|
+
|
|
48
|
+
- 用户提供的邮箱授权码本身通常拥有比插件开关更大的服务器权限。读/发开关是本插件的本地策略,不会缩小授权码在其他客户端中的权限。
|
|
49
|
+
- Full Access 会取消逐封发信弹窗。若希望审阅每封邮件,应使用普通权限;开启发信能力和选择 Full Access 的组合会授权 Agent 在该会话中直接发送。
|
|
50
|
+
- SMTP 没有通用幂等协议。DATA 阶段断线时服务器可能已经接收邮件;插件返回 `unknown`,但最终仍需人工检查。
|
|
51
|
+
- MIME 解析复杂。读取整封邮件前会先检查服务器报告的 message size,但恶意或错误服务器仍可能提供不一致信息。
|
|
52
|
+
- 本地附件下载会复核逻辑路径与真实路径,但仍依赖 Host 文件系统。受攻击的同用户本地进程仍可能与检查/写入竞争或访问同一用户可访问的文件;插件不提供 OS 级隔离。
|
|
53
|
+
|
|
54
|
+
## 安全问题报告
|
|
55
|
+
|
|
56
|
+
不要公开提交授权码、真实邮件或含个人信息的日志。请按 [SECURITY.md](../SECURITY.md) 的私密渠道报告。
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Tool contract
|
|
2
|
+
|
|
3
|
+
[简体中文](tool-contract.md) | **English**
|
|
4
|
+
|
|
5
|
+
## `mail_list_folders`
|
|
6
|
+
|
|
7
|
+
No input. Performs IMAP `LIST` and returns exact `path`, display name, delimiter, special-use marker, subscription state, and selectability. Historical mail may live in Archive, All Mail, or a provider-specific folder; callers use returned paths rather than guessing names.
|
|
8
|
+
|
|
9
|
+
The result is capped at 200 folders. `truncated=true` means the server reported more. The call does not open a mailbox or change message state.
|
|
10
|
+
|
|
11
|
+
## `mail_find`
|
|
12
|
+
|
|
13
|
+
The first call accepts optional `query`, `folder`, `limit` (1–50), `unreadOnly`, `since`, `until`, and `order` (`newest` or `oldest`). `folder` should be an exact path from `mail_list_folders`. Dates use the mail server's calendar-date semantics. `query` searches sender, recipients, CC, and subject on the server; it does not download bodies or build a local index.
|
|
14
|
+
|
|
15
|
+
Output includes `folder`, `order`, total `matched`, page `returned`, `hasMore`, optional `nextCursor`, and message rows. Each row contains an opaque handle, time, addresses, subject, unread and attachment indicators, and the server-reported size. Every string is untrusted mail data.
|
|
16
|
+
|
|
17
|
+
When `hasMore=true`, the next call supplies only `cursor=nextCursor` and optionally changes `limit`. Do not repeat or alter original filters. The cursor already binds folder, UIDVALIDITY, direction, boundary, and all filters. A UIDVALIDITY change invalidates the cursor and requires a new first call.
|
|
18
|
+
|
|
19
|
+
Tasks claiming a complete date range or “all related mail” must continue until `hasMore=false`. Search is per folder; query relevant archive folders separately.
|
|
20
|
+
|
|
21
|
+
## `mail_read`
|
|
22
|
+
|
|
23
|
+
Input is a message handle returned by `mail_find`. The tool validates folder, UIDVALIDITY, and UID; checks the whole-message size bound; then parses a plain-text body. HTML becomes plain text only when no text part exists, and remote resources are never loaded.
|
|
24
|
+
|
|
25
|
+
Output contains headers, `untrustedText`, a truncation flag, and opaque attachment handles. The call never writes flags.
|
|
26
|
+
|
|
27
|
+
## `mail_get_attachment`
|
|
28
|
+
|
|
29
|
+
Input is an attachment handle from `mail_read`. The tool revalidates the message and live MIME part and streams with a size cap.
|
|
30
|
+
|
|
31
|
+
Output contains a random workspace path, sanitized filename, MIME type, and actual size. The destination is fixed below `.dsh-mail-assistant/attachments/`. Before a native write the plugin resolves real paths and rechecks containment, then exclusively creates the file. It neither follows a pre-existing directory link outside the workspace nor overwrites an existing file.
|
|
32
|
+
|
|
33
|
+
## `mail_send`
|
|
34
|
+
|
|
35
|
+
Required input: `to[]`, `subject`, and `text`. Optional: `cc[]`, `bcc[]`, `attachments[]`, and `replyTo`. The tool sends plain text only. Addresses reject CR/LF; at most 50 addresses and ten attachments are accepted; attachments must be regular files in the current workspace and stay within the configured aggregate bound.
|
|
36
|
+
|
|
37
|
+
DSH's native `tools/pre-execute` waterfall decides permission. Ordinary presets request one approval; Full Access proceeds directly. Permission handling occurs before credentials, files, or SMTP access, and the independent sending capability must be enabled in either mode. `replyTo` only reads the original Message-ID to build `In-Reply-To`/`References`; recipients, subject, and body remain explicit.
|
|
38
|
+
|
|
39
|
+
Result status:
|
|
40
|
+
|
|
41
|
+
- `sent`: SMTP returned definite success;
|
|
42
|
+
- `unknown`: a connection-level failure occurred during DATA and the server may already have accepted the message.
|
|
43
|
+
|
|
44
|
+
Callers must never automatically retry `unknown`. Tool parameters do not provide a private approval bypass, and mail content is never interpreted as authorization.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 工具契约
|
|
2
|
+
|
|
3
|
+
**简体中文** | [English](tool-contract.en.md)
|
|
4
|
+
|
|
5
|
+
## `mail_list_folders`
|
|
6
|
+
|
|
7
|
+
无输入。通过 IMAP `LIST` 返回服务器实际存在的目录,包括精确 `path`、显示名、分隔符、special-use、订阅状态和是否可选择。历史邮件可能位于 Archive、All Mail 或服务商自定义目录,调用者不得猜目录名;应先用这里返回的 `path`。
|
|
8
|
+
|
|
9
|
+
最多返回 200 个目录,`truncated=true` 表示服务器目录数超过此安全上限。调用不会打开目录或修改状态。
|
|
10
|
+
|
|
11
|
+
## `mail_find`
|
|
12
|
+
|
|
13
|
+
首次输入:可选 `query`、`folder`、`limit`(1–50)、`unreadOnly`、`since`、`until`、`order`(`newest`/`oldest`)。`folder` 应使用 `mail_list_folders` 返回的精确路径;日期按邮箱服务器的日历日期语义处理。`query` 只在发件人、收件人、抄送人与主题上做服务器端查找,不默认下载正文建立本地索引。
|
|
14
|
+
|
|
15
|
+
输出:`folder`、`order`、当前条件的总匹配数 `matched`、本页条数 `returned`、`hasMore`、可选 `nextCursor` 和 message rows。每行包含 opaque `handle`、时间、地址、主题、未读标记、附件标记和服务器报告的大小。所有字符串均为不可信邮件数据。
|
|
16
|
+
|
|
17
|
+
当 `hasMore=true` 时,下一次只传 `cursor=nextCursor`,可选调整 `limit`;不得同时传入任何原始筛选条件。游标已经绑定 folder、UIDVALIDITY、翻页边界和完整筛选条件。UIDVALIDITY 变化会使游标明确失效,调用者应从无游标的首次查询重新开始。
|
|
18
|
+
|
|
19
|
+
需要覆盖某一时间范围或“所有相关邮件”时,调用者必须持续翻页到 `hasMore=false`。默认只搜索配置的收件箱;若可能存在归档邮件,应对相关目录分别查询。插件不跨目录构建本地索引。
|
|
20
|
+
|
|
21
|
+
## `mail_read`
|
|
22
|
+
|
|
23
|
+
输入:`mail_find` 返回的 `handle`。工具验证 folder/UIDVALIDITY/UID,检查整封邮件大小上限,再解析纯文本正文。HTML 仅在没有文本 part 时降级为纯文本,不加载远程资源。
|
|
24
|
+
|
|
25
|
+
输出:头部字段、`untrustedText`、截断标记,以及 opaque attachment handles。调用不会写 flags。
|
|
26
|
+
|
|
27
|
+
## `mail_get_attachment`
|
|
28
|
+
|
|
29
|
+
输入:`mail_read` 返回的 attachment handle。工具重新验证消息与 MIME part,并按上限流式下载。
|
|
30
|
+
|
|
31
|
+
输出:随机化后的工作区绝对路径、净化后的文件名、MIME type 和实际字节数。目标固定在 `.dsh-mail-assistant/attachments/`;本地写入前重新验证真实路径,随后使用独占创建,不跟随预置目录链接逃出工作区,也不覆盖任何现有文件。
|
|
32
|
+
|
|
33
|
+
## `mail_send`
|
|
34
|
+
|
|
35
|
+
输入:必填 `to[]`、`subject`、`text`;可选 `cc[]`、`bcc[]`、`attachments[]`、`replyTo`。只发送纯文本。地址不接受 CR/LF;最多 50 个地址、10 个附件,附件必须位于当前工作区,总大小受配置上限约束。
|
|
36
|
+
|
|
37
|
+
权限由 DSH 原生 `tools/pre-execute` 通路决定:普通权限请求一次性审批,Full Access 直接继续。权限通过后才读取附件或访问 SMTP;无论当前权限为何,邮件设置中的发信能力都必须已开启。`replyTo` 仅用于读取原邮件 Message-ID 并构造 `In-Reply-To`/`References`;仍然必须显式给出收件人、主题和正文。
|
|
38
|
+
|
|
39
|
+
输出状态:
|
|
40
|
+
|
|
41
|
+
- `sent`:SMTP 客户端收到确定成功结果;
|
|
42
|
+
- `unknown`:DATA 阶段发生连接级错误,邮件可能已被服务器接收。调用者不得自动重试。
|
|
43
|
+
|
|
44
|
+
工具参数不提供独立的审批开关,也不把邮件正文中的任何文本解释为发送许可。是否逐次审批只由当前 DSH 权限预设决定。
|