feat(ghost): 按 endpoint 收窄凭证注入 - #1719
Conversation
There was a problem hiding this comment.
Pull request overview
该 PR 为 Desktop 侧 Ghost 插件运行时补齐 network.secrets[].inject 的 endpoint 级收窄能力:在注入凭证时同时按 host、精确 URL.pathname 以及 HTTP method 三者匹配(省略新字段时保持旧的 host-only 语义),并在重定向/401 重试链路中逐跳重新匹配注入范围;同时同步相关校验、测试、文档与权限文案。
Changes:
- 在 manifest 校验与共享逻辑中引入
inject.paths/inject.methods(含规范化与 fail-closed 校验)以及 endpoint 匹配函数。 - 在
networkSlot注入链路中把注入判定从 host-only 扩展到 host+path+method,并在重定向与 401 重试中逐跳重算注入。 - 补充/更新单测、FORGE_GUIDE 文档示例及四语权限说明文案。
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/desktop/src/shared/ghost.ts | 新增 endpoint allowlist 校验/匹配逻辑,并将 scope 信息纳入权限展示 detail(仅在显式声明 paths/methods 时)。 |
| apps/desktop/src/main/cindy-brain/networkSlot.ts | 调整凭证注入与重定向/401 重试链路,按 host/path/method 逐跳重新判定注入。 |
| apps/desktop/src/shared/tests/ghost.test.ts | 覆盖 inject.paths/methods 的校验、归一化与权限 diff 行为。 |
| apps/desktop/src/main/cindy-brain/tests/networkSlot.test.ts | 覆盖 endpoint allowlist 注入、未命中时头剥离、同域重定向逐跳重算与 302 POST→GET 降级场景。 |
| apps/desktop/src/main/cindy-brain/forge.ts | 更新 FORGE_GUIDE 中 inject.paths / inject.methods 用法与语义说明。 |
| apps/desktop/src/renderer/i18n/locales/zh-CN/common.json | 更新凭证注入权限说明文案,提及可选 path/method 范围。 |
| apps/desktop/src/renderer/i18n/locales/en/common.json | 同步英文权限说明文案更新。 |
| apps/desktop/src/renderer/i18n/locales/ja/common.json | 同步日文权限说明文案更新。 |
| apps/desktop/src/renderer/i18n/locales/ko/common.json | 同步韩文权限说明文案更新。 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
f0663e1 to
ab8fc98
Compare
|
| Filename | Overview |
|---|---|
| apps/desktop/src/shared/ghost.ts | 新增 schemaVersion 3、endpoint 范围校验与权限详情归一化,并已修复既有 Hosts 顺序抖动。 |
| apps/desktop/src/main/cindy-brain/networkSlot.ts | 将凭证匹配扩展到 URL pathname 和实际 method,并在重定向与 401 流程中逐次重算和失效认证缓存。 |
| apps/desktop/src/shared/tests/ghost.test.ts | 覆盖新字段校验、版本门控、权限 diff、旧摘要兼容和 Hosts 稳定排序。 |
| apps/desktop/src/main/cindy-brain/tests/networkSlot.test.ts | 覆盖 endpoint 匹配、伪造头剥离、逐跳重定向和 method 降级后的 401 行为。 |
| apps/desktop/src/main/cindy-brain/forge.ts | 更新插件作者手册,说明 schemaVersion 3、精确 endpoint 范围及逐跳匹配语义。 |
| apps/desktop/src/renderer/i18n/locales/en/common.json | 更新英文凭证权限说明以反映可选路径和方法范围。 |
| apps/desktop/src/renderer/i18n/locales/ja/common.json | 更新日文凭证权限说明以反映可选路径和方法范围。 |
| apps/desktop/src/renderer/i18n/locales/ko/common.json | 更新韩文凭证权限说明以反映可选路径和方法范围。 |
| apps/desktop/src/renderer/i18n/locales/zh-CN/common.json | 更新中文凭证权限说明以反映可选路径和方法范围。 |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[插件网络请求] --> B{host 命中?}
B -- 否 --> F[剥离宿主管理凭证头后发送]
B -- 是 --> C{pathname 命中或未声明?}
C -- 否 --> F
C -- 是 --> D{method 命中或未声明?}
D -- 否 --> F
D -- 是 --> E[读取并注入凭证]
E --> G[发送请求]
G --> H{重定向?}
H -- 是 --> B
H -- 否 --> I{401 且允许安全重试?}
I -- 是 --> J[失效缓存并重新注入]
J --> G
I -- 否 --> K[返回响应]
Reviews (8): Last reviewed commit: "fix(ghost): 401 且 method 降级时仍失效被拒令牌缓存;FO..." | Re-trigger Greptile
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ab8fc9857f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (2)
apps/desktop/src/main/cindy-brain/networkSlot.ts:1128
- 这里先 new Map(initial*) 但紧接着每次请求后都会用 current* 覆盖 response* 引用,导致不必要的拷贝分配。可以直接用 initial* 引用初始化,减少分配并让语义更清晰。
let responseUsedExchange = initialUsedExchange;
let responseOauthInjected = new Map(initialOauthInjected);
let responseConnectionInjected = new Map(initialConnectionInjected);
apps/desktop/src/shared/ghost.ts:1791
- endpointScope 作为 permission item 的 detail 会在所有语言环境原样展示;这里硬编码的英文前缀 "Hosts/Paths/Methods" 会导致中文/日文/韩文界面出现混排。建议改成语言无关的键值格式(例如 hosts=/paths=/methods=)或避免英文前缀。
? [
`Hosts: ${(secret.inject.hosts ?? manifest.network?.hosts ?? []).join(', ')}`,
`Paths: ${secret.inject.paths?.join(', ') ?? '*'}`,
`Methods: ${secret.inject.methods?.join(', ') ?? '*'}`,
].join('\n')
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: da67e4b88d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (1)
apps/desktop/src/shared/ghost.ts:1791
- endpointScope 目前通过 GhostPermissionItem.detail 直接拼出
Hosts/Paths/Methods英文标签字符串;但 renderer 会把detail原样展示给用户(apps/desktop/src/renderer/cindy-brain/GhostPermissionList.tsx:116-120),因此在所有语言界面里都会出现英文技术标签。同时detail又参与权限 baseline/diff(ghostPermissionBaselineKey / diffGhostPermissionItems),后续若仅想调整展示文案也会被视作“权限变化”触发复核。建议把展示与指纹用途解耦:要么将 endpointScope 改成稳定、语言无关且更易读的机器格式(例如结构化/可解析的多行 key=value 或 JSON),要么在 GhostPermissionItem 上新增专用于 diff 的稳定字段,detail 保持面向用户的本地化文本。
const endpointScope =
secret.inject.paths !== undefined || secret.inject.methods !== undefined
? [
`Hosts: ${[...(secret.inject.hosts ?? manifest.network?.hosts ?? [])].sort().join(', ')}`,
`Paths: ${secret.inject.paths?.join(', ') ?? '*'}`,
`Methods: ${secret.inject.methods?.join(', ') ?? '*'}`,
].join('\n')
|
本 PR 触发了维护者确认门(product: feat 类型,新增插件凭证注入收窄策略)。 已创建讨论 issue,等待维护者确认。确认方式:
讨论 issue:#1948 |
|
@FIERsity 👋 这个 PR 还有 6 条 review conversation 没 resolve(apps/desktop/src/main/cindy-brain/networkSlot.ts / apps/desktop/src/shared/ghost.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。 如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。 |
|
@FIERsity 👋 这个 PR 目前与 请在本地 merge 最新的 |
为 network.secrets[].inject 增加可选 paths 与 methods 白名单;凭证仅在 host、pathname、method 同时命中时注入。缺省字段保持既有 host-only 行为。 初始请求、401 重试和每一跳重定向均按实际 URL 与 method 重新匹配;未命中 endpoint 时仍会剥除插件伪造的 Host 托管凭证头。同步 Forge 编写手册及四语 权限文案,并覆盖 manifest、运行时与重定向路径。 注意:这是 Draft 预览提交,尚不可合并/发布。旧 schema-v2 客户端会静默丢弃 未知 paths/methods,退回整域注入(fail-open)。必须先由维护者确定兼容发布 机制,并待 cindy-protocol#30 合入默认分支后再单独升级 submodule 指针。 验证: - pnpm test:unit(通过本地缓存 pnpm 10.26.2 运行;所有适用 workspace 通过) - desktop tsc --noEmit → 通过 - ghost/networkSlot/forge 定向测试 → 284 passed - check:endpoints / check:i18n / check:brand-terminology / check:i18n-glossary → 通过 Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: SAN <fierushio@gmail.com>
- 401 整链重试仅当最终响应 method 与原始请求 method 一致时进行: 3xx 把 POST 降级为 GET 并丢 body 后不再重放原始副作用请求。 - 每跳注入注释更新为按 host / pathname / method 重算的实际行为。 - inject.hosts 排序归一化,与 paths / methods 同款,权限 detail / diff 不再随声明顺序抖动。 Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: SAN <fierushio@gmail.com>
manifestDigest 按数组原始顺序计算(canonicalJson 不排序);对旧 host-only 清单排序会让已装插件的账本摘要永久失配,触发市场所有权 检查与 OIDC 签发拒绝(AGENTS.md 存量插件兼容红线)。 改为:仅当同一凭证声明 inject.paths / inject.methods 时排序 hosts (此时权限 detail 需要稳定);旧清单归一化输出与升级前逐字节一致。 权限 detail 的 Hosts 列表同样排序(Greptile P2),避免省略 hosts 时 按 network.hosts 声明顺序抖动。 Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: SAN <fierushio@gmail.com>
da67e4b to
3df6c69
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b9e71edc77
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…n fail-closed) v2 清单声明 inject.paths/methods 直接拒装:旧客户端不识别这两个字段, 放行会让收窄静默退化为整域注入(fail-open)。声明新字段即升级版本, 旧客户端对 v3 整包拒装(schemaVersion 严格相等检查),形成 fail-closed 边界。normalize 输出保留输入版本,v3 清单 manifestDigest 与打包时一致。 Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: SAN <fierushio@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: de7d319064
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (2)
apps/desktop/src/shared/ghost.ts:623
ghostSecretInjectMatches的method入参当前是string,内部再用method as GhostFetchMethod做断言。这里的调用方(networkSlot.handleFetchRequest)已经把 method 校验并收窄到GhostFetchMethod,建议把该函数签名也收窄到method: GhostFetchMethod并移除断言,避免未来出现未校验调用方传入任意字符串导致的静默不匹配。
method: string,
apps/desktop/src/shared/ghost.ts:1828
- 这里的 endpointScope 是主机生成的固定事实说明,但当前通过
detail字段以硬编码英文Hosts/Paths/Methods输出。按本文件对GhostPermissionItem的约定(ghost.ts:1602-1605),detail主要用于作者自由文本,主机固定说明应走 i18n(detailKey/detailArgs),否则会出现未本地化的用户可见文案,也会把“主机政策说明”和“作者说明”混在同一渲染通道里。建议把 endpointScope 改为结构化字段或新增一个detailKey(例如 networkSecretEndpointScopeDetail)并用 i18n 模板渲染 Hosts/Paths/Methods 行;OAuth scopes 仍可继续放在detail。
const endpointScope =
secret.inject.paths !== undefined || secret.inject.methods !== undefined
? [
`Hosts: ${[...(secret.inject.hosts ?? manifest.network?.hosts ?? [])].sort().join(', ')}`,
`Paths: ${secret.inject.paths?.join(', ') ?? '*'}`,
|
@FIERsity 👋 这个 PR 还有 3 条 review conversation 没 resolve(apps/desktop/src/shared/ghost.ts / apps/desktop/src/main/cindy-brain/forge.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。 如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。 |
- gh-cli 分支此前丢弃 endpointScope,扩大/替换限制不会进入权限 diff 与 更新确认(P1);现与其它来源一致把规范化事实写进 detail。 - FORGE_GUIDE:paths 注明 1–16 条上限;声明 paths/methods 必须 schemaVersion 3(主示例、network 段、拒装速查三处同步)。 Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: SAN <fierushio@gmail.com>
|
已处理:全部 10 条 review conversation 现已 resolve(0 未解决)。 本轮新出现的 3 条(codex 02:03–02:53 评论)已修复并推送(HEAD 04c8b72):
验证:ghost.test.ts(含新断言)178 个 + forge.test.ts 35 个全过,desktop tsc 0 错误。CI 将随新 push 重跑。 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 04c8b72432
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- P1:POST 经 3xx 降级为 GET 后 401,重放分支被 responseMethod === originalRequestMethod 拦截,但 exchange/OAuth 令牌缓存此前不清,后续 相同调用会一直复用被拒令牌、永远 401 无法刷新。新增对称失效分支 (与 Connection 分支同语义),并补两次调用回归测试:第二次必须重新 走交换端点取新令牌。 - P2:上轮 FORGE_GUIDE 加注释时多留了一个 }},导致示例 JSON 非法; 删除重复闭合括号。 Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: SAN <fierushio@gmail.com>
|
本轮 2 条新 P1 评论已修复并推送(HEAD 502cde7),thread 已全部 resolve(12/12):
验证:ghost 178 + networkSlot 99(含新测试)+ forge 35 = 312 全过,desktop tsc 0 错误。CI 随新 push 重跑。 |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (1)
apps/desktop/src/main/cindy-brain/networkSlot.ts:1244
- 这里在“Connection 401 且不允许重放”的分支里会直接 break,只作废了 Connection token 缓存;如果同一次请求同时注入了 exchange 凭证或 OAuth access token(responseUsedExchange / responseOauthInjected),它们的本地缓存不会被作废,后续调用可能持续复用被服务端拒绝的令牌并反复 401。建议在 break 前一并按本次实际注入情况失效 exchange / oauth 缓存。
this.deps.log?.info('ghost fetch-request Connection 401 cache invalidated without replay', {
ghostId, callId, method: responseMethod, host: url.hostname,
});
break;
}
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 502cde7089
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (raw.schemaVersion !== 2 && raw.schemaVersion !== 3) { | ||
| return { ok: false, reason: `schemaVersion 必须是 2 或 3,得到 ${JSON.stringify(raw.schemaVersion)}(v1 声明型已于 2026-07-12 移除)` }; |
There was a problem hiding this comment.
这里开始接受 schemaVersion: 3,但权威规则 docs/dev-rules/plugin-security-and-authoring.md:240-244 仍明确写着版本不等于 2 就会整份判无效,并据此分析降级行为;这会让后续作者和 reviewer 按已经失真的兼容性前提作判断。请在本次契约变更中同步该规则正文,说明 v2/v3 的接受范围及 v3 的降级结果。 docs/dev-rules/plugin-security-and-authoring.mdL29-L39
Useful? React with 👍 / 👎.
|
@FIERsity 👋 这个 PR 还有 1 条 review conversation 没 resolve(apps/desktop/src/shared/ghost.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。 如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。 |
|
@FIERsity 👋 这个 PR 目前与 请在本地 merge 最新的 |
这次改了什么
摘要
为
network.secrets[].inject增加可选的精确paths与 HTTPmethods白名单。宿主仅在 host、URL.pathname、实际请求 method 三者同时命中时注入凭证;省略新字段时,保持旧插件的 host-only 行为。变更类型
feat新功能fix缺陷修复refactor/perf重构或性能优化docs/test/chore文档、测试或工程维护范围
GhostSecretInjectDecl的paths/methods校验与规范化;FORGE_GUIDE作者手册和中英日韩权限文案同步。cindy-protocolsubmodule pointer。[Bug] create_branch 未传 from_ref 时错误固定使用 main,导致非 main 默认分支仓库返回 404 #30 的 commit 仅在 PR ref,标准 submodule fetch 不可解析;必须等协议仓合入公开默认分支、并确认服务端同步升级后单独更新;UI 变化
设计依据
docs/dev-rules/plugin-security-and-authoring.md§3、§4、§5、§6:凭证注入属于授权边界;存量未声明新字段的插件必须无感;作者可见 manifest 契约变更同步FORGE_GUIDE。docs/dev-rules/protocol-and-submodules.md§1:cindy-protocol是跨仓权威来源;submodule pointer 只能在协议公开可拉取、且服务端同步升级后更新。inject.paths/inject.methods最小扩展,而非引入新的 endpoint profile 抽象。怎么验证的
自动验证
手工验证
file-converter插件验证:移除未使用、整域注入的可选 secret 后,免费/mcp的docx查询返回 14 条路线,且不再要求该凭证。未执行的验证
风险
风险分类
影响与回滚
shared/ghost.tsmanifest 校验、networkSlot.ts凭证注入、作者手册和权限详情。paths/methods的 manifest 仍是既有 host-only 语义,安装、批准、指纹、布局和包格式均不改变。提交前检查
git commit -s,见 DCO)FORGE_GUIDE与权限文案)