问题
Ghost 的 network.secrets[].inject.hosts 当前按 hostname 匹配。若同一服务在同一 hostname 下同时提供公开端点和需要 API Key 的端点,manifest 无法表达“只对鉴权端点注入凭证”。
例如:
https://example.com/mcp:公开、无需认证
https://example.com/v1/convert:需要 Authorization: Bearer ...
以下声明会让两个端点都命中同一个 Secret:
{
"network": {
"hosts": ["example.com"],
"secrets": [{
"key": "api_key",
"inject": {
"header": "Authorization",
"format": "Bearer {value}",
"hosts": ["example.com"]
}
}]
}
}
实际表现
在一个文件转换 Ghost 中,/mcp 原本是免登录接口,但因为可选 API Key 的 inject.hosts 命中了整个域名:
- 未配置 Key 时,调用公开
/mcp 会在 Host 网络层直接失败,提示凭证未配置;
- 删除未使用的 Secret/全域注入声明后,同一个
list_conversions 请求立即成功;
- 实测
docx 查询成功返回 14 条转换路线。
插件当前可通过删除未使用的 Secret 规避,但如果后续同时启用同域的 /mcp 和 /v1/convert,插件侧没有安全、准确的表达方式。
期望
希望确认当前 host-only 行为是否为预期设计,并考虑为凭证注入增加更细粒度的显式 allowlist,例如:
- path 精确匹配或前缀匹配;
- 可选的 HTTP method 限定;
- 或 manifest 中独立的 endpoint/request profile。
安全语义建议:未匹配时不注入;重定向逐跳重新判断;不根据远端 401/403 自动重试并携带凭证。
需要考虑的兼容与安全项
- 旧 manifest 未声明新字段时保持当前行为;
- path 前缀边界(如
/api 不应误匹配 /api-public);
- URL 编码、query 是否参与匹配;
- 同域和跨域重定向时重新匹配;
- 未匹配端点绝不携带 Secret;
- 权限展示、安装批准状态和插件升级行为;
- Secret 不进入日志、返回值、沙箱或会话历史。
这是 manifest/权限边界变更,建议先讨论 schema,再决定是否实现核心 PR。
问题
Ghost 的
network.secrets[].inject.hosts当前按 hostname 匹配。若同一服务在同一 hostname 下同时提供公开端点和需要 API Key 的端点,manifest 无法表达“只对鉴权端点注入凭证”。例如:
https://example.com/mcp:公开、无需认证https://example.com/v1/convert:需要Authorization: Bearer ...以下声明会让两个端点都命中同一个 Secret:
{ "network": { "hosts": ["example.com"], "secrets": [{ "key": "api_key", "inject": { "header": "Authorization", "format": "Bearer {value}", "hosts": ["example.com"] } }] } }实际表现
在一个文件转换 Ghost 中,
/mcp原本是免登录接口,但因为可选 API Key 的inject.hosts命中了整个域名:/mcp会在 Host 网络层直接失败,提示凭证未配置;list_conversions请求立即成功;docx查询成功返回 14 条转换路线。插件当前可通过删除未使用的 Secret 规避,但如果后续同时启用同域的
/mcp和/v1/convert,插件侧没有安全、准确的表达方式。期望
希望确认当前 host-only 行为是否为预期设计,并考虑为凭证注入增加更细粒度的显式 allowlist,例如:
安全语义建议:未匹配时不注入;重定向逐跳重新判断;不根据远端 401/403 自动重试并携带凭证。
需要考虑的兼容与安全项
/api不应误匹配/api-public);这是 manifest/权限边界变更,建议先讨论 schema,再决定是否实现核心 PR。