AnujChhikara/cardinal

GitHub: AnujChhikara/cardinal

一个具备数据库感知能力的 TypeScript/JavaScript 静态分析工具,专注于在开发阶段发现并报告 N+1 查询、无界读取等低效数据库访问模式。

Stars: 0 | Forks: 0

# Cardinal [![VS Marketplace](https://img.shields.io/visual-studio-marketplace/v/anujchhikara.cardinal-vscode?label=VS%20Marketplace)](https://marketplace.visualstudio.com/items?itemName=anujchhikara.cardinal-vscode) [![npm](https://img.shields.io/npm/v/cardinal-cli?label=cardinal-cli)](https://www.npmjs.com/package/cardinal-cli) [![Open VSX](https://img.shields.io/open-vsx/v/anujchhikara/cardinal-vscode?label=Open%20VSX)](https://open-vsx.org/extension/anujchhikara/cardinal-vscode) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/AnujChhikara/cardinal/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) **一个具备数据库感知能力的 TypeScript/JavaScript 静态分析器。** 它会标记 低效的数据库访问 —— N+1 循环、过度获取、无界扇出 —— 就像 ESLint 一样,但专门针对数据层。100% 静态:没有 LLM,没有网络,没有 数据库连接;你的代码永远不会离开你的机器。 ## 目录说明 这是一个 pnpm monorepo: | Package | 功能说明 | |---------|--------------| | `cardinal-core` | 使用 ts-morph 解析 TS,将查询调用标准化为 `QueryDescriptor`s,运行规则,发出诊断信息。与前端无关。 | | `cardinal-cli` | 匹配文件,运行引擎,打印诊断信息,设置退出码(用于 CI 门控)。 | | `cardinal-vscode` | VS Code / Cursor / VSCodium 扩展 —— 实时诊断 + 抑制快速修复,共享同一个引擎。 | | `cardinal-website` | Astro 营销网站(本仓库的落地页)。 | ## 环境要求 - Node.js >= 18 - pnpm ## 构建与测试 ``` pnpm install pnpm build # builds all packages pnpm test # builds, then runs the full suite ``` ## 使用 CLI CLI 接受一个或多个**相对于当前目录的 glob 模式**,并且每个问题 打印一行,如果发现任何严重级别为 error 的诊断信息,则以 `1` 退出(因此它 可以作为 CI 门控)。 ``` # 从 repo 根目录开始,在执行 `pnpm build` 之后 node packages/cli/dist/bin.js "src/**/*.ts" ``` ### 试一试 创建一个包含 N+1 模式的文件: ``` // bad.ts const users = await prisma.user.findMany({ where: { active: true } }) for (const user of users) { const posts = await prisma.post.findMany({ where: { authorId: user.id } }) } ``` 运行: ``` node packages/cli/dist/bin.js "bad.ts" ``` 输出: ``` bad.ts:3:23 error n-plus-one Query on "post" runs inside a loop (N+1). Batch it into a single query (e.g. a WHERE ... IN / findMany). 1 problem(s), 1 error(s) ``` 修复后(单次查询,无循环)报告零问题并以 `0` 退出: ``` // good.ts const users = await prisma.user.findMany({ where: { active: true }, include: { posts: true } }) ``` 要全面了解 Prisma、Drizzle、Mongoose、TypeORM 和原生 SQL 的示例,请在 [`examples/anti-patterns.ts`](examples/anti-patterns.ts) 上运行 CLI。 ## 机器可读输出(用于 AI agent) Cardinal 不会重写你的代码 —— 但它会为 AI agent 提供 所需的一切。使用 `--format json` 运行,每条发现都包含其位置、消息, 以及可复用的 `why`/`fix` 解释: ``` node packages/cli/dist/bin.js --format json "src/**/*.ts" ``` ``` { "tool": "cardinal", "version": 1, "summary": { "problems": 2, "errors": 1 }, "findings": [ { "ruleId": "n-plus-one", "severity": "error", "file": "src/orders.ts", "line": 3, "column": 25, "message": "Query on \"post\" runs inside a loop (N+1)...", "explanation": { "why": "A query awaited inside a loop runs once per iteration — 1 + N round trips...", "fix": "Hoist the query out of the loop: collect the keys, run one batched query..." } } ] } ``` `message` 包含发现的具体细节(目标表,来自 你的知识文件的基数 —— 这是 agent 无法从源代码推断出的唯一信息); `explanation` 包含可复用的推理和修复方法。将 agent 指向它 (“运行 `cardinal check --format json` 并修复每个错误”)或将其发布到 PR 上。 状态通知会发送到 stderr,因此 stdout 始终保持纯 JSON。Cardinal 保持 100% 静态 —— 它负责发现和解释;你的 agent 负责应用编辑。 ## 规则 每条诊断信息都会链接到这里。严重级别是默认值 —— 你可以使用[配置文件](#configuration)覆盖它们中的任何一个(或 关闭某条规则)。 ### n-plus-one 在循环(`for`、`while`、`.map`/`.forEach`/`.flatMap`)内部 await 的查询 —— 1 + N 次往返。通过知识文件,可证明较小的循环会被静默,而 可证明较大的扇出会被提升级别。**error**(高置信度适配器)。 ### unbounded-read 没有过滤条件且没有 limit 的读取 —— 它可能会扫描整张表。**warning**。 ### over-fetch 在知识文件标记为*大型*的表上进行无过滤读取,而选择性 过滤本可以返回少得多的行。需要知识文件。**warning**。 ### order-by-rand `ORDER BY RAND()` / `RANDOM()` —— 对整个结果集进行排序并且无法使用 索引。**warning**。 ### leading-wildcard-like `LIKE '%…'` / `ILIKE '%…'` —— 前导通配符是非 sargable 的(全表扫描)。 **warning**。 ### excessive-joins 连接多张表的查询(JOIN 由真正的 SQL 解析器计算)。庞大的 join 扇出会给查询规划器带来负担。**warning**。 ### unindexed-query 在没有索引覆盖的列上进行过滤或排序的查询 —— 数据库会扫描 (或排序)整张表。Cardinal 直接从你的 **`schema.prisma`** 读取索引 (`@id`、`@unique`、`@@index`、`@@unique` —— 具备复合 前导列感知能力),像知识文件一样通过从当前目录向上遍历自动发现。 使用 `--schema ` 覆盖,使用 `--no-schema` 禁用。知识文件中标记该表为 small 会使其静默。 **warning**(目前支持 Prisma;下一步将支持更多 ORM)。 ``` app.ts:2:10 warning unindexed-query Query on "user" filters on "name", but no index has it as its leading column — the database scans the whole table. Add `@@index([name])` in schema.prisma. ``` ## 业务逻辑上下文 结构性规则看到的是*形状*,而不是*规模* —— 循环中的查询看起来像 N+1, 无论该循环运行两次还是两百万次。在你的代码旁边放置一个 **`cardinal.knowledge.yaml`**,为 Cardinal 提供缺失的 规模信息。这是一个静态的、人工编写的文件 —— 它保留在你的机器上 并且永远不会被传输。 ``` version: 1 tables: user: rows: 10000 filters: - when: { status: active } rows: 10 ``` **不要从空白文件开始 —— 运行 `cardinal init`。** 它会扫描你的代码并 为你构建知识文件框架:你查询的每个表,以及你的代码使用的确切过滤 子集(例如 `status = 'active'`),每个都带有一个可复制粘贴的 `count(*)` 查询。你只需填入数字。 ``` node packages/cli/dist/bin.js init # writes cardinal.knowledge.yaml ``` 有了这个文件,Cardinal 会**静默**处理可证明较小集合上的循环, **提升**可证明较大集合上的循环的严重级别,并在对大型表进行 无过滤读取而存在选择性替代方案时发出警告(`over-fetch`)。Cardinal 通过从当前目录向上遍历来发现该文件;使用 `--knowledge ` 覆盖或使用 `--no-knowledge` 禁用。 如果集合无法静态追踪,请为循环添加注解: ``` // cardinal: bounded 10 for (const id of getIds()) { await prisma.post.findMany({ where: { authorId: id } }); } ``` ### 抑制诊断信息 要静默特定的发现,请记录一条抑制信息,而不是修改代码: ``` node packages/cli/dist/bin.js suppress "src/contacts.ts:42" --reason "list is admin-curated, < 20" ``` 这会在知识文件中追加一条记录,通过规则 + 外层函数 + 标准化的调用文本(绝不是行号,因此它能承受调用上方的编辑)进行匹配。在不带 `--reason` 的情况下运行它以进行交互式提示。在 VS Code 扩展中,相同的流程是任何 Cardinal 波浪线上的灯泡快速修复。完整详细信息: [`docs/database-knowledge/business-logic-context.md`](docs/database-knowledge/business-logic-context.md)。 ### 报告错误的发现 Cardinal 是由真实的代码库调优的。如果发现是错误的 —— 误报、 遗漏的捕获或崩溃 —— 请使用其中一个 [issue 模板](https://github.com/AnujChhikara/cardinal/issues/new/choose)进行报告。 在 `cardinal suppress`(或 VS Code 抑制快速修复)之后,Cardinal 会提供一个 **预填写的报告链接** —— 在 GitHub 上查看并点击 Create;绝对不会 自动发送任何内容。每个确认的报告都作为永久的回归 测试发布在 [`packages/core/test/corpus/`](packages/core/test/corpus/) 中,因此修复后的 误报永远不会再次出现。 ## 配置 在你的项目中放置一个 **`cardinal.config.json`**(或 `.yaml`)以关闭规则 或更改其严重级别。它的发现方式与 知识文件一样,都是通过从当前目录向上遍历。 ``` { "rules": { "over-fetch": "off", "unbounded-read": "warning", "n-plus-one": "error" } } ``` 每条规则对应 `"error"`、`"warning"`、`"info"` 或 `"off"`。你没有 列出的规则将保持其默认行为。使用 `--no-config` 完全禁用发现功能。 VS Code 扩展会读取同一个文件,并在其发生更改时重新进行 lint。 ## 工作原理 Cardinal 围绕**三条通道的 pipeline** 设计,根据检查运行的*时间*进行划分: - **通道 1 — 语法**(每次击键):高置信度结构错误。`n-plus-one` 位于此处。 - **通道 2 — 常量**(每次击键):确定性的 DB/ORM 事实(例如 list-size limit)。 - **通道 3 — 数据流**(防抖/保存时):带置信度标记的警告。 指导原则:**精度优先于召回率** —— 一个谎报军情的 linter 当天 就会被禁用。高置信度检查视为 error;推断出的检查视为 warning。 设计详情:[`docs/superpowers/specs/2026-07-10-queryguard-design.md`](docs/superpowers/specs/2026-07-10-queryguard-design.md)(原始设计文档早于此次重命名)。 规则编写参考:[`docs/database-knowledge/`](docs/database-knowledge/)。 ## 路线图 已发布:配置文件、真正的 SQL 解析器、**跨所有适配器的上下文感知** (知识文件的 `over-fetch` / 基数现在适用于 Drizzle、Mongoose 和原生 SQL,而不仅仅是 Prisma)、**机器可读的 `--format json`** 输出(每个 发现都附带 why/fix 解释,以便 AI agent 应用修复), **Prisma 的 schema 感知**(`unindexed-query` 从 `schema.prisma` 读取索引),以及发布到 **VS Code Marketplace**、**Open VSX** 和 **npm**(在版本 tag 上自动化 —— 参见 [`PUBLISHING.md`](PUBLISHING.md))。下一步: - 为 Drizzle、TypeORM 和 Mongoose schema 提取索引(超越 Prisma 的 `unindexed-query`)。 - 更多由解析器支持的 SQL 规则:子查询、`HAVING`/`GROUP BY` 滥用、`SELECT *`。 - 更多引擎(MySQL/PlanetScale/Postgres limit)和数据层(Kysely、Sequelize)。 - 深度模式:跨模块数据流检查。 ## License MIT
标签:CMS安全, JavaScript, SOC Prime, TypeScript, 云安全监控, 安全插件, 开发工具, 性能优化, 数据可视化, 数据库, 检测绕过, 自动化攻击, 静态分析