vagoff/name-police

GitHub: vagoff/name-police

一种以命名约定模拟类型系统的 JavaScript 静态分析工具,无需类型推断即可强制执行核心类实例的命名规范和流动追踪。

Stars: 0 | Forks: 0

# 🚔 name-police **阻止你的队友将 DB 实例命名为 `helper2`。** name-police 是一个 JavaScript 静态分析工具,它以类型系统的力度强制执行命名规范——却无需背负类型系统的重量。 ``` test/service.js:42 — "conn" receives new DB_ToolBox() but doesn't match required pattern ^(dbt|new_dbt|dbt2|tmp_dbt)$ test/service.js:67 — "plain" receives "dbt" (controlled as DB_ToolBox) but doesn't match required pattern test/service.js:89 — "dbt" is a controlled name and can only be used in assignments or method calls. Found in: IfStatement ``` ## 理念 每个代码库都有其核心类。`DB_ToolBox`。`ApiClient`。`AuthSession`。你确切地知道它们是什么——你也知道当有人将实例命名为 `temp`、`obj` 或 `x` 时,事情会变得一团糟。 name-police 做出了一个简单的观察: 其数学逻辑如下: - `new DB_ToolBox()` **只能**被赋值给匹配 `^(dbt|...)$` 的变量 - `dbt*` 变量**只能**流入另一个 `dbt*` 变量或参数 - `dbt*` 名称**只能**出现在赋值、调用和空值检查中 - 因此:**任何名为 `dbt*` 的变量都保证持有一个 `DB_ToolBox` 实例** 无需类型推断。无运行时开销。无需博士学位。 这不是匈牙利命名法。匈牙利命名法通过将类型编码到没人要求的前缀中来增加认知负荷。name-police 允许你使用**好名字**——简短、可读、显而易见——然后强制执行它们。`dbt` 只是数据库工具箱的一个好名字。机器检查的不变量是一个免费的奖励。 ### 一个名字。一个约定。无处不在。 name-police 强制执行第二个不变量,使整个系统协同工作: 如果 `process(dbt, userId)` 存在于任何地方,那么每个文件中的每个 `process()` 都必须以相同的顺序接受相同的参数。两个类拥有一个含义不同的 `process`?重命名其中一个。 这听起来很严格。确实如此。这也是 name-police 能够在**不知道哪个类拥有该方法**的情况下检查调用点的原因。当你编写 `svc.process(helper, 42)` 时,name-police 不需要解析 `svc`——它在全局签名表中查找 `process` 并知道参数 1 必须是 `dbt*`。无需类型推断,无需类层次结构遍历。 副作用:它迫使你名副其实。一个 `process` 总是意味着同一件事的代码库是一个更好的代码库。 ## 安装 ``` npm install --save-dev name-police ``` ## 用法 ``` npx name-police .name-police.json src/**/*.js ``` ## 配置 ``` { "strictConstructors": true, "allowedConstructors": ["EventEmitter", "Router"], "constructors": { "DB_ToolBox": "^(dbt|new_dbt|dbt2|tmp_dbt)$", "ApiClient": "^api[A-Z][A-Za-z0-9]*$", "AuthSession": "^(sess|session|authSess)$" } } ``` ## 捕获的问题 ### ✗ 构造时名称错误 ``` const helper = new DB_ToolBox(); // ERROR: "helper" doesn't match ^(dbt|...)$ const dbt = new DB_ToolBox(); // ✓ this.dbt = new DB_ToolBox(); // ✓ ``` ### ✗ 受控名称泄漏到非受控变量 ``` const plain = dbt; // ERROR: "plain" is not a controlled name const dbt2 = dbt; // ✓ ``` ### ✗ 受控名称用于禁止上下文 ``` console.log(dbt); // ERROR const x = dbt + something; // ERROR `query: ${dbt}`; // ERROR return dbt; // ERROR if (dbt) { ... } // ✓ null-check is fine dbt.query(); // ✓ method call is fine const { dbt } = obj; // ✓ shorthand destructuring preserves the name return { dbt }; // ✓ shorthand object property preserves the name ``` ### ✗ 将受控实例传递给错误的参数 ``` class Service { process(dbt, userId) { ... } // pass1 records: process → [dbt, userId] } svc.process(helper, 42); // ERROR: arg 1 must match ^(dbt|...)$ svc.process(dbt, 42); // ✓ svc.process(this.dbt, 42); // ✓ last segment is "dbt" ``` ### ✗ 方法签名冲突 ``` // file1.js class A { process(dbt, userId) {} } // file2.js class B { process(conn, userId) {} } // ERROR: "process" has conflicting signatures ``` 这是一项功能,而非限制。一个名字 = 一个约定。始终如一。 ### ✗ 未知构造函数(当 `strictConstructors: true` 时) ``` const repo = new UserRepository(); // ERROR: not in constructors or allowedConstructors const m = new Map(); // ✓ built-in const e = new Error('oops'); // ✓ built-in ``` ## 工作原理 对你的 AST 进行两遍扫描: **Pass 1** — 全局收集所有文件中的函数、方法和 mixin 签名。构建表:`methodName → [paramName, paramName, ...]`。标记任何具有两个不同签名的名称。 **Pass 2** — 运行 5 个独立的访问器: 1. `strict-constructors` — 未知的 `new Foo()` 检查 2. `constructor-naming` — 检查每个 `new` 的赋值目标 3. `assignment-flow` — 检查赋值的每一侧 4. `call-arg-naming` — 根据签名表检查每个调用点 5. `forbidden-contexts` — 受控名称允许位置的白名单 每个访问器都是独立的。添加新规则 = 添加一个文件。 ## 支持的语法 - 类、类字段、类方法 - Mixin:`const M = (Super) => class extends Super { ... }` - 对象字面量方法:`const o = { process(dbt) { } }` - 任意嵌套层级的命名函数和箭头函数 - 带有受控参数的匿名回调 → **禁止**(请为你的函数命名) - `this.dbt`、`o.x.dbt` — 最后一段是受控名称 - 默认参数:允许 `f(dbt = null)`,禁止 `f(dbt = 5)` - 简写解构:允许 `const { dbt } = obj` - `Object.assign` 和 `.prototype` → 始终禁止 ## ESLint 插件 ``` npm install --save-dev eslint-plugin-name-police ``` ``` { "plugins": ["name-police"], "rules": { "name-police/enforce": ["error", { "config": ".name-police.json" }] } } ``` ## 前期工作 没有东西完全做到这一点。最接近的是: - **TypeScript 品牌类型** — 更重,不强制命名,需要 TS - **Semgrep** — 没有流分析的模式匹配,YAML 规则不可组合 - **ESLint naming-convention** — 孤立地检查名称,没有流追踪 - **匈牙利命名法** — 同样的想法,没有强制执行,增加噪音 name-police 是你观察到**命名规范 + 机械强制执行 = 类型系统提供的大部分功能**时的产物,而成本仅为后者的一小部分。 ## 许可证 MIT ## 作者 由 [@vagoff](https://github.com/vagoff) 和 [Claude](https://claude.ai) (Anthropic) 在一次会话中构建。 想法、架构以及每一个“等等,你没处理这种情况”—— vagoff。 输入 —— Claude。 ## idealib — 想法库 此代码库包含从构建 name-police 中提取的原子想法单元库——关于静态分析、架构、命名以及与 LLM 协作。 **[→ Idea Library Index](https://github.com/vagoff/name-police/blob/main/INDEX.md)** — 完整列表及 blob URL,可由代理和 LLM 获取。 `idealib_en/` — English · `idealib_ru/` — Russian · `idealib_attic/` — 已废弃 ### 克隆后的设置 ``` git config core.hooksPath .githooks ``` 这将启用 pre-push hook,在每次推送时自动重新生成 `INDEX.md`。
标签:CMS安全, JavaScript, Linting, LNA, MITM代理, SOC Prime, 云安全监控, 代码安全, 代码规范, 变量命名, 命名规范, 开发工具, 数据可视化, 数据管道, 无类型推断, 漏洞枚举, 类型系统, 自动化payload嵌入, 自定义脚本, 软件工程, 静态分析, 静态检查