拒绝下载也要关流
Agent 平台做网页抓取、文件摄取和多模态附件下载时,安全边界不能只停在“我拒绝了这个 URL / 这个 Content-Length / 这个状态码”。Vercel AI SDK 6.0.207 和 5.0.204 刚修了一个很典型的 provider-utils 问题:下载在早期被拒绝后,如果没有取消 response body,WHATWG Fetch / undici 会把底层 TCP socket 留着,攻击者控制的源站就可以把“被拦截的下载”变成文件描述符耗尽。
这个点对 Agent Engineer 很实用,因为 Agent runtime 往往会把下载包装成高级能力:读取网页、解析 PDF、抓取图片、跟随跳转、接 MCP 或浏览器工具返回的 URL。我们容易在策略层写很多 allowlist、size limit、redirect validation,却忘了每一个拒绝分支本身也是资源生命周期分支。Vercel 这次修复的关键不是换一个校验规则,而是在 readResponseWithSizeLimit、download、downloadBlob 以及 validated redirects 的每个早退路径里显式 cancel body;连被拒绝或将继续跟随的 redirect hop,也要先释放它自己的响应体。
工程上可以把它抽象成一条运行时契约:凡是 Agent 可触发的外部 I/O,如果结果不会被完整消费,就必须有明确的 close / cancel / abort 语义,并且这件事要进入测试。否则“安全拒绝”只是业务上拒绝了,系统资源层仍然在接受对方施压。OPC 这类平台以后做 connector、browser、file ingestion 或 remote tool proxy 时,下载 helper 最好只暴露一种封装过的结果类型,让调用方无法绕过 finally cleanup。
可以回头检查一下:你现在的 Agent 工具里,哪些路径会在发现状态码不对、文件太大、MIME 不对、跳转不可信时直接 return 或 throw?这些路径有没有真的把网络连接、文件句柄、临时文件和后台任务都关掉?