npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rust-security-auditor

v0.1.7

Published

Local stdio MCP server for focused Rust security review.

Readme

Rust Security Auditor

English · 简体中文

一个面向 Rust 项目的本地安全审查 server,通过 MCP 协议对话。它审查 unsafe 与 FFI 面、Cargo 供应链与构建期的信任边界、以及进程执行——既可以审计整个项目,也可以只审你正要提交的这次改动。

它完全运行在你的机器上,读取本地 Cargo 项目,从不修改目标源码树,向 Claude Code、Codex、Cursor 或任何 MCP 客户端暴露五个只读工具。

AI 辅助开发写 Rust 的速度,已经超过了人工审查的速度。生成的模块因为"更快"而用上 get_unchecked,生成的 build.rs 调起外部命令,生成的 Cargo.toml 把依赖钉成 *——每一行都是编译器接受的正常 Rust。这个工具把它们找出来,并告诉你每一处要求你证明什么。

两种用法

审计整个项目 —— 接手一个 crate、评估某个依赖、或准备发版时用。rust_audit_project 会在 Cargo 真正会编译的代码上跑全部规则;rust_audit_unsaferust_audit_dependencies 各自收窄到一个面。你拿到的是完整清单:unsafe 在哪、每处承担什么义务、构建期存在哪些信任边界。

审查这次改动 —— 每次提交前用,尤其是 agent 刚生成完代码之后。rust_review_current_diffgit diff,把「这次改动引入的」和「本来就有的」分开。在一个已有几百条发现的 crate 上,这就是「一份你会忽略的清单」和「一份你会处理的清单」之间的差别。

两者跑的是同一套规则。全项目审计告诉你现在站在哪;diff 审查防止你往回滑。

下面是第二种用法在一次真实改动上的输出节选:

# Rust Security Review: Current Diff

## Decision

NEEDS ATTENTION

- Safe to commit: No
- Reason: No hard blockers were found, but introduced findings or directly
  related same-function/same-unsafe-site context need human review before commit.
- Blocking findings: 0
- Manual review findings: 2

## Introduced by this diff

### Unsafe site at src/buffer.rs:11

- Location: `src/buffer.rs:11`
- Function/context: `read_fast`
- Diff relation: introduced_by_diff
- Findings:
  - Generic unsafe block (RSA-UNSAFE-BLOCK, medium severity/high pattern-detection confidence)
  - get_unchecked skips bounds checking (RSA-UNSAFE-GET-UNCHECKED, medium severity/high pattern-detection confidence)

同一文件里其它位置的既有 unsafe 代码会被单独归类,不会看起来像新增的阻塞项。

是什么让输出可用

一个什么都报的安全工具,只会被读一次。三个设计决定把输出压到你真正会处理的量级,每一条背后都有实测数据。

只审 Cargo 会编译的代码。 BurntSushi/memchr 的默认审计原本报 1721 条,其中 1311 条来自单个 1.6 MB 的基准测试输入文件——那个文件存在的目的是被搜索,不是被编译。现在文件按 Cargo 的可达方式分类,全项目审计只读 src/build.rs,数字变成 396,其中 374 条来自真实的 crate 源码。排除永不静默——每份报告都会说明排除了什么、为什么,一个开关就能全部找回来。

diff 审查把「引入的」和「本来就有的」分开。 tokio-rs/bytes 全仓 246 条发现,仅 src/bytes.rs 一个文件就有 77 条。往这个文件里加五行、含一个 unsafe 块,审查报出的是一条新引入的发现,外加一条因为落在同一个 unsafe 块里、确实相关的既有发现——另外 76 条被隐藏。

看不到的时候,它拒绝说「通过」。 如果你 diff 里的某个文件读不了、超限、或落在项目根之外,审查会明说,并且不给结论——而不是基于残缺的扫描报一个干净结果。同样的原则适用于当前平台无法无歧义寻址的 Git 路径:直接让调用失败,而不是去审查那个路径碰巧命中的文件。

一切都在你机器上运行。它只读本地路径,从不写入你的源码树,不发起任何网络请求。

能检出什么

Unsafe 与 FFIRSA-UNSAFE-BLOCKRSA-UNSAFE-FNRSA-UNSAFE-IMPL-SENDRSA-UNSAFE-IMPL-SYNCRSA-FFI-EXTERN-CRSA-FFI-CSTR-FROM-PTRRSA-UNSAFE-TRANSMUTERSA-UNSAFE-MAYBEUNINITRSA-UNSAFE-FROM-RAW-PARTSRSA-UNSAFE-SET-LENRSA-UNSAFE-BOX-FROM-RAWRSA-UNSAFE-GET-UNCHECKEDRSA-UNSAFE-UNCHECKED-CALLRSA-UNSAFE-STATIC-MUTRSA-UNSAFE-RAW-PTR-ACCESS

供应链与构建RSA-DEP-GITRSA-DEP-PATHRSA-DEP-PROC-MACRORSA-DEP-BUILD-DEPENDENCIESRSA-DEP-LOCK-GITRSA-DEP-VERSION-UNBOUNDEDRSA-BUILD-SCRIPTRSA-BUILD-COMMANDRSA-CARGO-SOURCE-REPLACEMENTRSA-CARGO-RUNNER

运行时执行RSA-EXEC-COMMAND

每条发现都带有规则 id、文件和行号、证据、为什么重要、一个具体的风险场景,以及建议的修复方式。发现按 file + startLine + ruleId 去重,并按严重度、置信度、位置排序。

扫描器会跟踪 Rust 的注释和字面量边界,所以块注释、文档示例或字符串字面量里的模式不会被上报。#[cfg(test)] 内的发现会降低严重度,因为测试代码不参与发布。

置信度指的是模式检出的确定性,不是可利用性。 高置信度的意思是「这个模式确实在那里」,不是「确认存在漏洞」。

扫描范围

广度审计读取 Cargo 真正会编译的内容:每个 crate 的 src/,加上 build.rs。它跳过 test、benchmark、example 目标,也跳过任何 Cargo 目标都到不了的 .rs 文件——样例输入、vendor 快照、临时草稿。永不参与编译的代码不可能带来运行时风险,扫描它只会把真正重要的发现淹没。

跳过永不静默。每份报告都会说明排除了多少文件、以及为什么:

Excluded 18 Rust file(s) from source scanning: 18 file(s) no Cargo target reaches.
Set includeNonShippedSources to include them.

rust_audit_projectincludeNonShippedSources: true 就会把它们纳入。rust_review_current_diff 从不应用这个过滤——你改了测试目标,那是你有意改的,所以照审。

能告诉你什么,不能告诉你什么

它能告诉你:一个 crate 的 unsafe 和 FFI 面在哪里、每个位置各自承担什么义务;某次具体改动引入了什么、以及哪些既有代码近到值得一并看;构建期和供应链的信任边界在哪——构建脚本、git 和 path 依赖、过程宏、registry 替换、自定义 target runner;以及有哪些风险是别人已经接受过的,包括那些已经过期的接受记录。

它不能告诉你某个 unsafe 块是否真的不健全。它指出那些「内存安全依赖于编译器不检查的不变量」的位置,把证据和问题交给你。证明那个不变量成立,仍然是你或你的评审者的工作。

已知短板

  • 不查已知漏洞。 没有 RustSec advisory 或 CVE 查询,所以它永远不会告诉你某个依赖版本有已公开的安全公告。请配合 cargo auditcargo deny 使用。(已列入 roadmap
  • 带词法上下文的模式匹配,不是语义分析。 没有 AST、类型信息、数据流或污点追踪。它知道某一行是代码而不是注释或字符串,也知道这一行落在哪个函数和哪个 unsafe 块里。但它不知道一个指针是从哪来的。
  • 风险等级反映的是数量和严重度,不是可利用性。 一个刻意使用 unsafe 的 crate——SIMD、分配器、FFI 绑定——会被标成 high_risk,因为它发现多,不是因为它危险。memchr 在真实源码里有 374 条发现;memchr 没问题。看发现,别看那个标签。
  • 已经评审过、写了文档的 unsafe 块仍然会被报出来。 附近的 SAFETY: 注释会降低置信度,但不会移除这条发现,因为工具无法检查那条注释说的是不是真的。这正是「已接受风险抑制」存在的意义。
  • #[tokio::test] 会被当作生产代码,除非它位于 #[cfg(test)] 模块内。只有 Rust 自带的 #[test]、以及确定要求 testcfg 才会降低发现的严重度——其它任何属性路径都可能是一个在 release 构建里照样编译的宏。
  • 依赖审查读的是清单文件,不是解析后的依赖图。 它检查 Cargo.tomlCargo.lockbuild.rs.cargo/config.toml,但不解析传递依赖、不检查被 yank 的 crate、不评估 feature 合并。
  • 不是形式化验证、符号执行,也不能替代人工评审 unsafe 不变量。
  • 不是托管服务、SaaS 扫描器或代码上传扫描器。它只读本地路径。
  • 不是通用代码评审或风格检查工具。

安装

需要 Node.js 20 或更高版本。不需要 Rust 工具链——扫描器读源码和清单文件,不构建你的项目。

主路径:npx

不用克隆,也不用构建。让 MCP 客户端直接指向已发布的包:

{
  "command": "npx",
  "args": ["--yes", "rust-security-auditor"]
}

npx 会在首次使用时拉取并缓存。要锁定版本,把包名写成 [email protected]

发布由 GitHub Actions 通过 npm trusted publishing 完成,因此这个包不存在任何长期有效的发布 token,且每个已发布版本都带有 provenance 证明,把 tarball 和产出它的那次提交与工作流运行绑定在一起。用 npm view rust-security-auditor dist.attestations 可以看到。

备选:本地 checkout

想跑改过的代码,或者所在环境不接受启动时从 registry 拉取,走这条。

git clone https://github.com/lihytaihe-lang/rust-security-auditor.git
cd rust-security-auditor
npm ci
npm run build

然后让客户端指向这份 checkout 构建出的 server。它的标准输入输出保留给 JSON-RPC,日志走 stderr。

{
  "command": "node",
  "args": ["/absolute/path/to/rust-security-auditor/dist/src/mcp/server.js"]
}

要审查的 Rust 项目通过工具参数 projectPath 传入绝对路径;server 进程本身不需要运行在那个项目目录下。两条路径都一样。

客户端配置

这个 server 讲的是标准 stdio MCP,所以下面的配置块到处都一样,区别只是放进哪个文件。CI 在 Linux、macOS 和 Windows 上都验证了 stdio 边界本身——握手、tools/list、以及一次真实的 tools/call。各客户端的配置格式来自各家官方文档,没有逐一在宿主 UI 里实际跑过,所以如果哪家改了格式,以它的文档为准。

Claude Desktop 是唯一需要单独说明的:它当前的流程期待的是 Desktop Extension,而本项目不提供 .mcpb,所以暂时没有针对 Desktop 的配置可给。

Claude Code

claude mcp add --transport stdio rust-security-auditor -- npx --yes rust-security-auditor

Codex CLI / app / IDE 扩展

# ~/.codex/config.toml 或某个可信项目的 .codex/config.toml
[mcp_servers.rust_security_auditor]
command = "npx"
args = ["--yes", "rust-security-auditor"]

对应的 Codex CLI 命令:

codex mcp add rust-security-auditor -- npx --yes rust-security-auditor

Cursor

{
  "mcpServers": {
    "rust-security-auditor": {
      "command": "npx",
      "args": ["--yes", "rust-security-auditor"]
    }
  }
}

Cursor 的配置块放进项目的 .cursor/mcp.json,或用户级的 ~/.cursor/mcp.json

VS Code / Copilot

{
  "servers": {
    "rustSecurityAuditor": {
      "command": "npx",
      "args": ["--yes", "rust-security-auditor"]
    }
  }
}

VS Code 的配置块放进 .vscode/mcp.json,或通过 MCP: Open User Configuration 打开的用户级 mcp.json

Qoder、ZCode 和 Kimi Code 用的是与 Cursor 相同的 mcpServers 结构,区别只在放进哪个文件:

| 宿主 | 配置块放在哪 | | --- | --- | | Qoder | MCP 设置 → + Add 会打开一个 JSON 编辑器,粘贴后保存 | | ZCode | .zcode/config.json——它也能直接导入已有的 Claude Code MCP 配置 | | Kimi Code CLI | ~/.kimi-code/mcp.json,或项目内的 .kimi-code/mcp.json;用 /mcp-config 也能对话式添加 |

各家的配置格式和文件位置各自独立演进,使用前请复查各自的官方文档。

机器可读版本见 examples/mcp-client-config.jsonexamples/codex-plugin-config.json

上面每个配置块用的都是 npx。想改用本地 checkout,把 command 换成 node、参数换成那份 checkout 的 dist/src/mcp/server.js 绝对路径即可。

不接客户端、只想调试 checkout 的话,npm --silent run mcp 会重新构建并启动。

怎么用

配置好之后,你就正常跟 agent 说话。它会自己挑工具、把项目路径传进去,你只负责看结果。三种场景基本覆盖全部用法。

接手一个代码库,或者评估一个 crate。 让它做一次全项目审计,并给出项目路径:

审计一下 /path/to/my-rust-project 的安全风险。

你会拿到:总体风险等级、按严重度和规则分类的计数、首要发现、分组的审查信号,以及哪些区域该优先看。代码对你是新的、你在决定要不要依赖某个 crate、或者准备发版时,从这里开始。

提交之前,尤其是刚生成完代码时。 项目已经打开的话,直接说:

提交前帮我审一下当前的改动。

你会拿到 block / needs_attention / pass 三选一的结论、这次改动引入了什么、以及近到值得一并看的既有代码。这个是建议每次都跑的——改动干净时它很安静,所以常驻在流程里几乎没有成本。

发版前,清点已经接受过的风险。 抑制记录带着负责人、工单号和过期日期:

列一下已接受的风险,有过期的告诉我。

怎么读一条发现

每条发现都回答四个问题,你不用翻规则源码就能判断:发现了什么、在哪;证据行;为什么重要、可能出什么事;以及建议怎么修。然后分三种情况:

  • 确实是问题 —— 修。建议的修复和建议的测试是起点,不是定论。

  • 没问题,而且你说得出为什么 —— 把这个判断记录在代码里,而不是无视它:

    // rust-security-auditor: ignore RSA-UNSAFE-BLOCK owner=@you ticket=SEC-123 until=2026-12-31 -- 指针由调用方校验,已在 PR #42 评审
    unsafe { *ptr }

    理由是必填的。它会过期,过期后这条发现会重新出现在报告里——所以这是一份审计轨迹,不是静音键。

  • 工具错了 —— 那是个值得报的 bug。带上最小复现片段开一个 误报 issue

一个刻意使用 unsafe 的 crate 被标成 high_risk,意思是「发现很多」,不是「很危险」。看发现本身。

五个工具

| 工具 | 用途 | | --- | --- | | rust_review_current_diff | 这次改动引入了什么?提交前、开 PR 前、以及 agent 生成代码之后跑。 | | rust_audit_unsafe | unsafe 块和函数、FFI 边界、裸内存原语、unsafe Send/Sync。 | | rust_audit_dependencies | Cargo 清单、锁文件、构建脚本、git/path 依赖、过程宏、.cargo/config.toml。 | | rust_audit_project | 覆盖全部规则的本地广度扫描。 | | rust_list_accepted_risks | 已接受风险的抑制注释清单,含已过期和无效的。 |

用自然语言调用即可——「提交前审一下当前 diff」「审查 unsafe」「检查依赖」「列出已接受的风险」——也可以直接传参:

{
  "projectPath": "/absolute/path/to/rust/project",
  "baseRef": "main",
  "headRef": "HEAD",
  "staged": false,
  "includePreExisting": false,
  "nearChangedLineWindow": 3,
  "outputFormat": "markdown",
  "pathMode": "relative",
  "reportMode": "compact"
}

每个工具都接受 projectPathoutputFormatmarkdown | json)、pathModerelative | absolute)和 reportModecompact | full)。默认是 relativecompact——这样你粘进 PR 的内容里不会带上本机绝对路径。

各工具的脱敏示例输出见 examples/reports/

当前 diff 审查

rust_review_current_diff 默认审查工作区;传 staged: true 审查 git diff --cached;同时给出 baseRefheadRef 时审查 baseRef..headRef

每条发现按它与本次改动的关系分类:

| 关系 | 含义 | | --- | --- | | introduced_by_diff | 起始于新增行。 | | same_unsafe_site_context | 既有代码,但与新增行处于同一个 unsafe 块。 | | same_function_context | 既有代码,同一函数内,不同的 unsafe 位置。 | | nearby_legacy_context | 行号接近新增行,但位于不同的函数或 unsafe 位置。 | | unrelated_nearby | 行号接近新增行,但没有确认的关联。 | | pre_existing_in_changed_file | 位于被改动的文件内,但在改动行窗口之外。 |

compact 输出显示 introduced_by_diffsame_unsafe_site_context,以及中高置信度下中等及以上严重度的 same_function_contextnearby_legacy_context 被隐藏,这样另一个函数里的老 unsafe 代码不会看起来像新的阻塞项——用 reportMode: "full" 可以看到它们,用 includePreExisting: true 可以把被改文件里的历史发现也纳入。如果附近的发现仍然吵,把 nearChangedLineWindow 降到 1 或 2。

工具还会返回一个 reviewDecision

  • block — 引入了非低置信度的 critical/high 发现。
  • needs_attention — 引入了 medium 发现、直接相关的同 unsafe 位置或同函数上下文、低置信度的引入项,或存在已过期/无效的抑制。
  • pass — 没有阻塞项,也没有需要人工复核的项。

结论主要由 introduced_by_diff 驱动。同一 unsafe 位置的 high 发现需要关注但不会硬阻塞;除非你显式要求纳入既有发现,否则 nearby_legacy_contextunrelated_nearby 不会影响结论。

已接受风险的抑制

抑制是「已评审的误报」或「有意接受的风险」的记录,不是用来藏掉未解决的阻塞项的。

pub fn read_byte(ptr: *const u8) -> u8 {
    // rust-security-auditor: ignore RSA-UNSAFE-BLOCK owner=@security ticket=SEC-123 until=2026-12-31 -- legacy FFI wrapper reviewed in host project
    unsafe { *ptr }
}
// rust-security-auditor: ignore RULE_ID -- reason
// rust-security-auditor: ignore RULE_ID until=YYYY-MM-DD -- reason
// rust-security-auditor: ignore RULE_ID owner=@name ticket=SEC-123 -- reason
  • -- 之后的理由是必填的,RULE_ID 必须是精确的规则 id。
  • 不支持 ignore allignore *
  • 过期的抑制会重新上报;无效的抑制会被忽略并列出来待清理。
  • 旧的 rustsec-auditor: 标记仍然可用但已废弃——它与无关的 RustSec 项目撞名。请把已有注释改成 rust-security-auditor:;扫描器遇到旧写法时会给出警告。

rust_list_accepted_risks 可以在不跑完整扫描的情况下清点有效、过期和无效的抑制。它的 JSON 和 Markdown 输出都包含扫描覆盖信息;如果覆盖不完整,请把结果当作「不完整的清单」,而不是「不存在已接受风险」的证明。

报告模式

compact(默认)面向 agent 摘要和 PR 评论:总体风险、严重度与规则计数、首要发现、分组的审查信号、高优先区域,以及建议的后续提示词。JSON 里的 findings 数组始终是完整的。

full 保留每条发现的全部细节——证据、为什么重要、风险场景、建议修复、建议测试、参考资料、误报说明和抑制记录。适合审计笔记、交接归档和抑制复核。

当多条发现指向同一个 unsafe 位置时,Markdown 会把它们归到那个位置下。这只是显示层的分组,JSON 不变。

开发

npm ci
npm run typecheck
npm test
npm run check      # typecheck + 测试 + 空白字符检查

不接 MCP 客户端的本地调试:

npm run mcp:call -- rust_audit_project --projectPath test/fixtures/vulnerable-rust-project --outputFormat markdown
npm run mcp:call -- rust_review_current_diff --projectPath /absolute/path/to/rust/project --staged true

构建产物在 dist/,已被 git 忽略。新增规则的流程见 CONTRIBUTING.md

源码结构:

| 路径 | 内容 | | --- | --- | | src/mcp/server.ts | 本地 stdio MCP server | | src/mcp/tools.ts | 由扫描内核支撑的工具处理函数 | | src/scanners/rustLexer.ts | 注释/字面量遮罩与测试代码识别 | | src/scanners/unsafeScanner.ts | unsafe、FFI 和裸内存规则 | | src/scanners/dependencyScanner.ts | Cargo 清单、锁文件、构建脚本与 cargo 配置规则 | | src/scanners/sourceRiskScanner.ts | 运行时进程执行 | | src/scanners/rules.ts | 规则元数据:严重度、原理、修复建议 | | src/scanners/suppressions.ts | 抑制解析与过期判定 | | src/git/diffParser.ts | 统一 diff 解析器 | | src/reports/ | Markdown 与 JSON 渲染、发现的 schema |

安全模型

server 会校验 projectPath 存在且是本地目录,只在其内部扫描,把 git diff 路径过滤为安全的相对路径,拒绝可能被当作命令行开关解析的 git ref,并且在不经过 shell 的情况下调用 git。它不上传代码、不打包源码、不访问任何网络服务。

发现阶段会跳过 .gittargetnode_modules 等目录,不跟随符号链接,在打开目录之后立即重新校验一次,并对单文件大小、文件数、目录数、总字节数和读取并发做上限。在返回源码字节之前,读取器会校验规范化后的包含关系、拒绝含符号链接的路径分量、确认路径名仍然解析到已打开的那个文件,并把读取长度限制在该文件描述符已验证的大小内。覆盖信息在一次工具调用内是单调的:可选的上下文提取不能把一个不完整的改动输入变成完整覆盖。畸形的 Rust 词法输入会禁用「仅测试代码」的严重度下调,并把覆盖标记为不完整。覆盖信息在 JSON 和 Markdown 中都是结构化的;当前 diff 若存在不完整的 Rust/Cargo 输入,会以 needs_attentionsafeToCommit: false 失败关闭。

关于如何报告本工具自身的漏洞、以及什么在范围内什么不在,见 SECURITY.md

状态

Apache-2.0 许可。最新发布是 v0.1.7,已发布到 npm,包名 rust-security-auditor。计划中的内容和有意排除在外的内容见 ROADMAP.md