跳到正文 Hacktron 登录 [S]
Hacktron 登录 [S] 客户 博客 文档 联系我们 登录

Hacktron 如何为 Novu 的安全团队带来信心,而不是更多流程


Novu
Hacktron Review

, , X, LinkedIn

分享

Hacktron 如何为 Novu 的安全团队带来信心,而不是更多流程

XLinkedIn
2分钟
修复 28 个安全问题
在最初 30 天内
83% 的发现
需要处理,而非噪音
NoSQL 注入
被其他工具遗漏

“就像有一位渗透测试人员在审查你提交的每一个 PR。”

Dima Grossman
首席技术官

关于 Novu

Novu 是一个开源通知基础设施平台,通过单一 API 在 15 个以上的渠道(应用内、邮件、短信、推送)发送通知,既用于云端部署,也用于物理隔离的本地部署。该项目的开源版本拥有 39,000 个 GitHub 星标和 400 多名外部贡献者。如今,Novu 正通过 Novu Connect 拓展到双向智能体通信,让 AI 智能体直接在 Slack 等渠道中完成任务,而不只是把用户引导到别处。

挑战:信任一个有 400 名贡献者参与的代码库

Novu 对每一项外部贡献都执行零信任策略。外部贡献者超过 400 名,任何代码都必须经过严格审查才能发布。两件事正在同时发生:AI 让 Novu 交付的代码越来越多,而团队对合规驱动的供应商安全审查究竟能发现什么也越来越怀疑。

“我做的年度供应商审查越多,就越不信任它们。他们只是对所有客户公司套用同一份十项检查清单,发现的通常都是些无关紧要的东西。

“这能把该打的勾都打上,但在内部,我们对此毫无信任。”

Dima Grossman
首席技术官

Novu 已经部署了一套分层工具(CodeQL、Greptile、Cursor 的安全审查器,以及定期的 DeepSec 扫描),在两个检查点运行:每次提交 PR 时,以及发布打标签之前再运行一次。

“有了 AI,我们在产品功能面上的产出更多了。

“这当然也让审查中人为失误的因素高了很多。”

Dima Grossman
首席技术官

Hacktron 如何融入:带来信心,而不是改变流程

对于哪些变了、哪些没变,Dima 说得很直接:

“引入 Hacktron 后,我们的流程没有任何改变。真正改变的只有我们的信心:我们知道自己能发现复杂得多的模式。

“而这种信心也让我们能够提升速度。”

Dima Grossman
首席技术官

这种信心归根结底来自信号质量。Novu 现有的工具大多只标记 PR diff 中可见的问题(Dima 称之为“一跳”漏洞利用)。Hacktron 会为仓库构建代码图,并在不同 PR 之间比较这些图的差异,从而发现二阶问题,也就是只有追踪某个文件中的改动如何影响系统其他位置的代码时才会显现的漏洞。

“看到其他工具的评论时,很多情况下那些问题要么不在范围内,要么属于可接受的风险。

“看到 Hacktron 的评论时,团队会认真对待、仔细思考。很多时候我们发现它们相当有价值。”

Dima Grossman
首席技术官

Dima 还点出了他正在积极防范的风险:噪音疲劳。

“谁都不希望它变成那个喊‘狼来了’的孩子。

“报告里的通知一多,人就会开始抓不住重点。”

Dima Grossman
首席技术官

在这里,信号质量并非锦上添花。它决定了一款工具是被团队认真阅读,还是被团队学会忽视。

发现分析

Novu 使用 Hacktron 的最初 30 天(2026 年 7 月 6 日至 8 月 6 日):

230 个拉取请求61 个发现中 42 个已分诊28 个已解决1 个有效发现6 个已接受风险7 个误报 230 个拉取请求61 个发现中 42 个已分诊28 个已解决1 个有效发现6 个已接受风险7 个误报

另有 18 个发现仍在等待分诊,还有 1 个因来源 PR 被放弃而撤回。

它能发现哪些类型的漏洞?

Novu 自有的工具大多只能发现单个文件或单个 PR diff 中可见的问题。Dima 特别看重的那些发现,只有在跨多个文件追踪一次调用时才会显现,而这正是基于模式匹配的扫描器无从标记的问题。

NoSQL 注入和跨对话授权绕过(高危)

在 PR #12062 中发现,并在合并前于同一 PR 中修复。

当时 Novu 正在推出一条新的摄取路径,让自托管的 AI 智能体向平台发送结构化事件(消息、回复、审批)。该管道的一部分需要通过匹配客户端提供的消息 ID,把传入事件解析回对应的对话活动:

private async resolveActivityByClientId(
environmentId: string,
conversationId: string,
messageId: string
): Promise<ConversationActivityEntity | null> {
for (let attempt = 0; attempt < ACTIVITY_RESOLVE_MAX_ATTEMPTS; attempt += 1) {
const activity = await this.activityRepository.findOne(
{ _environmentId: environmentId, identifier: messageId },
'*'
);
if (activity) {
return activity;
}
if (attempt < ACTIVITY_RESOLVE_MAX_ATTEMPTS - 1) {
await delay(ACTIVITY_RESOLVE_DELAY_MS);
}
}
return this.activityRepository.findByPlatformMessageId(environmentId, conversationId, messageId);
}

这里有两个问题叠加在一起。第一,查询过滤条件只检查了环境,从未检查该活动属于哪个对话。第二,传入的 messageId 在直接用于该过滤条件之前,没有被验证为普通字符串,这意味着它可以被替换成 MongoDB 查询运算符,而不是 ID。两者结合,已认证的调用者就能解析并进而编辑或删除不属于自己的对话中的活动。

Hacktron Review 通过追踪横跨三个文件的调用链发现了这个问题:接收原始事件载荷的摄取控制器,未做深度验证就将其转换为类型化事件的用例,以及把未经验证的 ID 传入上述查询的 sink 服务。

成果:有信心交付得更快,而不是更慢

在这个有 400 多名外部贡献者参与的代码库上,Novu 获得了持续的审查,没有那种让团队不再阅读安全工具输出的噪音疲劳,还获得了让他们交付得更快而非更慢的信心。

客户

联系我们

预约演示或免费开始。

[↓] [↑] 按 [enter] 打开
菜单
[M]
菜单中的下一项
[J]
菜单中的上一项
[K]
搜索
[F]
提问
[A]
登录
[S]
复制页面
[C]
收听
[L]
两端对齐
[R]
语言
[G]
深色模式
[D]
高对比度
[H]
快捷键
[I]
关闭
[Esc]