Checklist
🐞 问题详细描述
1. 问题概述
ReactAgent webhook 测试发现:在 callback 能力已经启用时,调用方可以通过SendMessage.params.pushNotificationConfig.callbackUrl 提交任意合法格式的 HTTP/HTTPS URL。即使目标 host不在受信任 runtime、registry、allowlist 或部署配置中,runtime 仍返回 JSON-RPC result,而不是在 Task 创建前返回trust-policy error 或 -32602 Invalid params。
本次使用的 callback URL 为:
http://untrusted.invalid/a2a/push-notifications/callback
PR 151 最新提交已经解决原问题 :当 pushNotifications=false 时,inline push config 会返回-32003 Push Notification is not supported。测试继续执行后,在“不可信 callback target”子场景失败,说明能力关闭门控已生效,但 callback target 的 trusted-host 校验仍未实现。
代码核查确认,当前 A2aPushNotificationCallbackUrlPolicy 只校验 URL 是否为绝对 URI、是否存在 host,以及 scheme 是否为HTTP/HTTPS;没有读取或匹配 trusted-host/allowlist 配置。现有模块测试甚至明确把https://evil.example/... 视为可进入 SDK handler 的合法 URL。
2. 当前 runtime 代码实现
2.1 PR 151 已增加能力关闭门控
文件:
service/agent-service-app/src/main/java/com/openjiuwen/service/app/controller/a2a/A2aJsonRpcController.java
当前代码在 inline push config 存在时先检查部署开关:
if (!a2aProperties.isPushNotifications()) {
throw new PushNotificationNotSupportedError();
}
A2aPushNotificationCallbackUrlPolicy.validateCallbackUrl(
params.configuration().taskPushNotificationConfig());
该修改解决了原问题,但后续 URL policy 没有可信 host 输入,因此不能解决本问题。
2.2 URL policy 只校验 URL 格式
文件:
service/agent-service-app/src/main/java/com/openjiuwen/service/app/controller/a2a/A2aPushNotificationCallbackUrlPolicy.java
当前实现:
URI uri = new URI(callbackUrl);
String scheme = uri.getScheme();
if (!uri.isAbsolute() || scheme == null || uri.getHost() == null || uri.getHost().isBlank()
|| (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme))) {
return Optional.empty();
}
return Optional.of(uri);
这只能拒绝空值、相对 URL、无 host URL 和非 HTTP/HTTPS scheme。untrusted.invalid 和 evil.example 都是语法合法的HTTP host,因此会被放行。
2.3 当前没有 trusted-host/allowlist 实现
在 PR 151 的 service 模块中搜索 trustedHost、trusted-host、trusted hosts 和 allowlist,没有找到对应生产实现或配置项。
当前 A2AProperties、controller 和 URL policy 之间也没有传递可信 host 集合,因此系统无法区分:
- 受信任的 runtime callback endpoint;
- 普通 client 自报的任意公网 URL;
- 内网地址、回环地址或其他可能引发 SSRF 的目标。
2.4 现有模块测试与设计要求相反
文件:
service/agent-service-app/src/test/java/com/openjiuwen/service/app/controller/a2a/A2aJsonRpcControllerTest.java
测试 absoluteHttpInlinePushConfigReachesSdkHandler() 使用:
https://evil.example/a2a/push-notifications/callback
并断言:
verify(requestHandler).onMessageSend(...);
这实际上把“任意绝对 HTTP/HTTPS URL”作为可接受行为固定了下来,与 FEAT-001/L2 的 trusted-host 要求冲突。该测试需要改为:
- allowlist 内 host 可以进入 handler;
- allowlist 外 host 必须返回错误且
verifyNoInteractions(requestHandler)。
2.5 sender 复用同一弱 URL policy
HttpPushNotificationSender 同样调用 A2aPushNotificationCallbackUrlPolicy.callbackUri()。因此即使将 ingress 拒绝遗漏,outbound sender 也没有第二道 trusted-host 防线,存在向任意 HTTP/HTTPS host 投递 Task 结果的风险。
3. 当前实现与设计的冲突
| 对比项 |
文档预期 |
当前实现/测试表现 |
| callback URL 语法 |
必须合法 |
已校验 absolute URI、host、HTTP/HTTPS |
| callback host 信任 |
必须来自 trusted runtime/registry/allowlist/部署配置 |
未实现,任意合法 HTTP/HTTPS host 被接受 |
| ingress 拒绝时机 |
保存配置和创建 Task 前 |
不可信 URL 进入 SDK handler |
| Task/config 副作用 |
不可信时不得创建异步执行、不得保存配置 |
response 不含 error,未在 ingress 拦截 |
| outbound 防线 |
不得向不可信 URL 投递 |
sender 复用只校验格式的 policy |
| Agent Card capability |
依赖 trusted hosts 可用性 |
当前没有 trusted-host 配置参与能力计算 |
| 模块测试 |
覆盖 allowlist 内允许、allowlist 外拒绝 |
当前断言 evil.example 应进入 handler |
4. 建议修改方向
4.0 修改项关系说明
| 修改项 |
是否必须 |
关系说明 |
| 4.1 定义 trusted-host 配置与匹配规则 |
必须 |
没有可信来源就无法执行信任判定 |
| 4.2 ingress 在 Task 创建前校验 |
必须 |
直接满足 L2 的无副作用拒绝要求 |
| 4.3 sender 投递前再次校验 |
必须 |
防止存量配置、旁路写入或竞态绕过 ingress |
| 4.4 capability 绑定 trusted-host 可用性 |
必须 |
确保 Agent Card 不夸大能力 |
| 4.5 SSRF 安全规则 |
必须明确 |
需要覆盖 DNS、IP、端口和重定向,而不只是字符串后缀 |
| 4.6 补充测试 |
必须 |
验证允许和拒绝路径均无回归 |
推荐整体落地:统一 trusted-target policy + ingress 前置校验 + sender 二次校验 + capability gate 接入 + 完整模块/黑盒测试。
4.1 定义可信 callback target 配置
建议由 registry 或部署配置提供受信任 runtime endpoint。若首迭代采用静态配置,应至少明确:
- 允许的 scheme;
- 精确 host 或 CIDR,不使用宽泛字符串后缀匹配;
- 允许的端口;
- 固定 callback path;
- 是否允许回环、私网和 link-local 地址;
- DNS 解析及重绑定防护;
- HTTP redirect 是否禁止或每一跳重新校验。
示意配置名可由开发按现有规范确定,例如:
openjiuwen:
service:
a2a:
push-notifications: true
trusted-callback-targets:
- scheme: http
host: 127.0.0.1
port: 18080
path: /a2a/push-notifications/callback
该名称只是建议,不应在未确认配置规范前作为最终契约。
4.2 ingress 在 Task 创建前校验
建议 controller 调用一个可注入配置的统一 policy:
private void validateInlinePushNotificationConfig(MessageSendParams params) {
TaskPushNotificationConfig config = inlinePushConfig(params);
if (config == null) {
return;
}
if (!a2aProperties.isPushNotifications()) {
throw new PushNotificationNotSupportedError();
}
trustedCallbackTargetPolicy.requireTrusted(config.url());
}
requireTrusted() 应同时完成 URL 语法和可信 endpoint 校验。校验失败必须抛出可映射为 trust-policy error 或 -32602 的A2AError,并确保 requestHandler.onMessageSend() 未被调用。
4.3 sender 投递前二次校验
HttpPushNotificationSender 应在每次 HTTP 请求前复用同一个 trusted-target policy,而不能只依赖 ingress。这样可以防止:
- 历史或外部写入的非法 config;
- Task/config store 被其他路径绕过;
- DNS 或 registry 状态变化;
- redirect 将请求导向不可信地址。
校验失败应记录可观察的 delivery failure,不得向目标发包,也不得改变 Task 终态。
4.4 capability 绑定 trusted-host 可用性
只有在 trusted target 来源已经配置并可用时,Agent Card 才能声明 pushNotifications=true。否则应保持 false,避免出现能力已声明但任何合法 callback endpoint 都无法通过,或未配置 allowlist 却允许任意 URL 的状态。
4.5 补充模块和黑盒测试
建议至少覆盖:
- callback 能力关闭:返回
-32003 且 handler 无交互;
- callback 能力开启 + allowlist 内精确 endpoint:允许进入 handler;
- callback 能力开启 + allowlist 外 HTTP/HTTPS host:返回 trust-policy error/
-32602,handler 无交互;
- scheme、host 或 port 不匹配:拒绝;
- path 不匹配:若采用固定 endpoint 契约则拒绝;
- DNS 解析到 loopback/private/link-local:按明确策略拒绝;
- HTTP redirect 到不可信 host:禁止跟随或重新校验后拒绝;
- sender 收到旁路写入的不可信 config:不发起 HTTP 请求;
- 拒绝时 config store 无新增记录、TaskStore 无新增 Task;
- Agent Card capability 与 trusted-host 配置完整性一致。
现有 absoluteHttpInlinePushConfigReachesSdkHandler() 应改为可信 endpoint 测试;evil.example 应进入拒绝测试。
详细的环境信息描述
agent-runtime-java PR 151: d127595
提交说明: fix(a2a): satisfy code check exception boundary
其他辅助信息
版本信息
感谢您的贡献 🎉!
Checklist
🐞 问题详细描述
1. 问题概述
ReactAgent webhook 测试发现:在 callback 能力已经启用时,调用方可以通过
SendMessage.params.pushNotificationConfig.callbackUrl提交任意合法格式的 HTTP/HTTPS URL。即使目标 host不在受信任 runtime、registry、allowlist 或部署配置中,runtime 仍返回 JSON-RPCresult,而不是在 Task 创建前返回trust-policy error 或-32602 Invalid params。本次使用的 callback URL 为:
PR 151 最新提交已经解决原问题 :当
pushNotifications=false时,inline push config 会返回-32003 Push Notification is not supported。测试继续执行后,在“不可信 callback target”子场景失败,说明能力关闭门控已生效,但 callback target 的 trusted-host 校验仍未实现。代码核查确认,当前
A2aPushNotificationCallbackUrlPolicy只校验 URL 是否为绝对 URI、是否存在 host,以及 scheme 是否为HTTP/HTTPS;没有读取或匹配 trusted-host/allowlist 配置。现有模块测试甚至明确把https://evil.example/...视为可进入 SDK handler 的合法 URL。2. 当前 runtime 代码实现
2.1 PR 151 已增加能力关闭门控
文件:
service/agent-service-app/src/main/java/com/openjiuwen/service/app/controller/a2a/A2aJsonRpcController.java当前代码在 inline push config 存在时先检查部署开关:
该修改解决了原问题,但后续 URL policy 没有可信 host 输入,因此不能解决本问题。
2.2 URL policy 只校验 URL 格式
文件:
service/agent-service-app/src/main/java/com/openjiuwen/service/app/controller/a2a/A2aPushNotificationCallbackUrlPolicy.java当前实现:
这只能拒绝空值、相对 URL、无 host URL 和非 HTTP/HTTPS scheme。
untrusted.invalid和evil.example都是语法合法的HTTP host,因此会被放行。2.3 当前没有 trusted-host/allowlist 实现
在 PR 151 的
service模块中搜索trustedHost、trusted-host、trusted hosts和allowlist,没有找到对应生产实现或配置项。当前
A2AProperties、controller 和 URL policy 之间也没有传递可信 host 集合,因此系统无法区分:2.4 现有模块测试与设计要求相反
文件:
service/agent-service-app/src/test/java/com/openjiuwen/service/app/controller/a2a/A2aJsonRpcControllerTest.java测试
absoluteHttpInlinePushConfigReachesSdkHandler()使用:并断言:
这实际上把“任意绝对 HTTP/HTTPS URL”作为可接受行为固定了下来,与 FEAT-001/L2 的 trusted-host 要求冲突。该测试需要改为:
verifyNoInteractions(requestHandler)。2.5 sender 复用同一弱 URL policy
HttpPushNotificationSender同样调用A2aPushNotificationCallbackUrlPolicy.callbackUri()。因此即使将 ingress 拒绝遗漏,outbound sender 也没有第二道 trusted-host 防线,存在向任意 HTTP/HTTPS host 投递 Task 结果的风险。3. 当前实现与设计的冲突
evil.example应进入 handler4. 建议修改方向
4.0 修改项关系说明
推荐整体落地:统一 trusted-target policy + ingress 前置校验 + sender 二次校验 + capability gate 接入 + 完整模块/黑盒测试。
4.1 定义可信 callback target 配置
建议由 registry 或部署配置提供受信任 runtime endpoint。若首迭代采用静态配置,应至少明确:
示意配置名可由开发按现有规范确定,例如:
该名称只是建议,不应在未确认配置规范前作为最终契约。
4.2 ingress 在 Task 创建前校验
建议 controller 调用一个可注入配置的统一 policy:
requireTrusted()应同时完成 URL 语法和可信 endpoint 校验。校验失败必须抛出可映射为 trust-policy error 或-32602的A2AError,并确保requestHandler.onMessageSend()未被调用。4.3 sender 投递前二次校验
HttpPushNotificationSender应在每次 HTTP 请求前复用同一个 trusted-target policy,而不能只依赖 ingress。这样可以防止:校验失败应记录可观察的 delivery failure,不得向目标发包,也不得改变 Task 终态。
4.4 capability 绑定 trusted-host 可用性
只有在 trusted target 来源已经配置并可用时,Agent Card 才能声明
pushNotifications=true。否则应保持 false,避免出现能力已声明但任何合法 callback endpoint 都无法通过,或未配置 allowlist 却允许任意 URL 的状态。4.5 补充模块和黑盒测试
建议至少覆盖:
-32003且 handler 无交互;-32602,handler 无交互;现有
absoluteHttpInlinePushConfigReachesSdkHandler()应改为可信 endpoint 测试;evil.example应进入拒绝测试。详细的环境信息描述
agent-runtime-java PR 151: d127595
提交说明: fix(a2a): satisfy code check exception boundary
其他辅助信息
版本信息
感谢您的贡献 🎉!