yarrumretep/ts-prefix-internals

GitHub: yarrumretep/ts-prefix-internals

一个利用 TypeScript 类型信息为内部符号添加前缀的 CLI 工具,使压缩器能够安全地混淆属性名,从而减小生产包体积并增加逆向难度。

Stars: 1 | Forks: 0

# ts-prefix-internals [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/a8a7abf620024213.svg)](https://github.com/yarrumretep/ts-prefix-internals/actions/workflows/ci.yml) 一个 TypeScript CLI 工具,它为内部符号添加 `_` 前缀,以便 terser 或 esbuild 可以通过 `mangleProps: /^_/` 激进地混淆它们。 ## 问题所在 像 terser 和 esbuild 这样的 JavaScript 压缩工具可以混淆局部变量名,但它们**无法安全地混淆属性和方法名**。它们无法知道哪些名称是内部实现细节,哪些是公共 API 或 DOM/库接口的一部分。混淆错误的属性名会在运行时破坏你的代码。 这意味着类属性、方法名和其他成员标识符在你的生产包中保持未混淆状态,这通常占标准压缩后剩余代码大小的很大一部分。 ## 解决方案 TypeScript 的编译器 API *确实*知道哪些符号是内部的。它拥有完整的类型信息,了解导出、类可见性修饰符以及整个依赖图。 `ts-prefix-internals` 利用这些信息来: 1. 从桶导出入口点**发现你的公共 API 表面** 2. 将项目中的**每个符号分类**为公共或内部 3. 使用 TypeScript Language Service 进行安全的跨文件重命名,**为内部符号添加 `_` 前缀** 4. **验证输出**编译无错误 添加前缀后,你配置压缩器混淆所有匹配 `/^_/` 的内容: ``` // terser { mangle: { properties: { regex: /^_/ } } } // esbuild { mangleProps: /^_/ } ``` ## 会被添加前缀的内容 **内部(将被添加前缀):** - 未从入口点导出的类、函数和变量 - 导出类的私有成员 - 非导出类的所有成员 - 内部接口、类型别名和枚举 **公共 API(将不会被添加前缀):** - 从入口点文件导出的符号 - 导出类的公共/受保护成员 - 导出接口和枚举的成员 - 在公共 API 签名中引用的类型(递归跟踪) - 来自 `node_modules` 或 `.d.ts` 文件的任何内容 - 已经以 `_` 开头的符号 ## 安装 ``` npm install -D ts-prefix-internals ``` ## 使用方法 ### CLI ``` # 预览重命名内容 npx ts-prefix-internals -p tsconfig.json -e src/index.ts -o .prefixed --dry-run # 重命名并输出 npx ts-prefix-internals -p tsconfig.json -e src/index.ts -o .prefixed # 多入口点 npx ts-prefix-internals -p tsconfig.json -e src/index.ts -e src/server.ts -o .prefixed ``` ### 选项 ``` -p, --project Path to tsconfig.json (required) -e, --entry Public API entry point file(s), repeatable (required) -o, --outDir Output directory for rewritten files (required) --prefix Prefix string (default: "_") --dry-run Report what would be renamed without writing files --verbose Print every rename decision with reasoning --skip-validation Skip post-rename type-check --force Continue despite dynamic-access errors (exit 0) -h, --help Show help ``` ### 编程 API ``` import { prefixInternals } from 'ts-prefix-internals'; const result = await prefixInternals({ projectPath: 'tsconfig.json', entryPoints: ['src/index.ts'], outDir: '.prefixed', prefix: '_', dryRun: false, verbose: false, skipValidation: false, }); console.log(`Prefixed ${result.willPrefix.length} symbols`); console.log(`Kept ${result.willNotPrefix.length} public API symbols`); if (result.validationErrors) { console.error('Output has type errors:', result.validationErrors); } ``` ## 示例 给定一个具有桶导出的项目: ``` // src/index.ts export { Processor } from './engine'; export { Coord, CoordKind } from './types'; ``` 以及一个未从桶中导出的内部类: ``` // src/graph.ts export class LinkMap { private forward: Map>; connect(a: string, b: string): void { /* ... */ } } ``` 运行该工具会产生: ``` WILL PREFIX (internal): LinkMap class graph.ts:1 -> _LinkMap LinkMap.forward property graph.ts:2 -> _forward LinkMap.connect method graph.ts:4 -> _connect Processor.links property engine.ts:5 -> _links Processor.pending property engine.ts:6 -> _pending WILL NOT PREFIX (public API): Processor class engine.ts:4 (exported) Processor.setEntry method engine.ts:15 (public member) Coord interface types.ts:1 (exported) Coord.ns property types.ts:2 (interface member) ``` 输出目录包含有效的 TypeScript,其中所有内部符号都已添加前缀,可以进行激进的压缩。 ## 工作原理 1. **API 表面发现** (`api-surface.ts`) -- 从入口点桶导出开始,递归发现每个公共符号。解析别名链 (`export { Foo } from './foo'`),遍历类型签名以查找引用的类型,包括公共/受保护的类成员、接口成员和枚举成员。 2. **符号分类** (`classifier.ts`) -- 遍历每个源文件并对每个符号进行分类。使用公共 API 集来确定什么是内部的。处理私有类成员、构造函数参数属性,并为动态属性访问生成警告。 3. **重命名** (`renamer.ts`) -- 使用 TypeScript Language Service `findRenameLocations` API 进行安全的跨文件重命名。首先收集所有编辑,去重,按逆序排序,并自下而上应用以避免位置偏移。 4. **验证** -- 使用 `tsc --noEmit` 编译输出以验证重命名是安全的。 ## 构建流水线集成 使用此工具的典型构建流水线: ``` # 1. Prefix internal symbols npx ts-prefix-internals -p tsconfig.json -e src/index.ts -o .prefixed # 2. Compile the prefixed source cd .prefixed && tsc # 3. Bundle and mangle with terser/esbuild esbuild .prefixed/dist/index.js --bundle --minify --mangle-props=_ ``` 或者作为 package.json 脚本: ``` { "scripts": { "prefix": "ts-prefix-internals -p tsconfig.json -e src/index.ts -o .prefixed", "build": "npm run prefix && cd .prefixed && tsc && esbuild dist/index.js --bundle --minify --mangle-props=_" } } ``` ## 发布 ``` # patch release (默认) npm run release # minor/major/prerelease npm run release -- minor ``` 发布脚本行为: 1. 验证你在 `main` 分支上且工作树是干净的 2. 运行测试 3. 使用 `npm version` 提升版本 4. 推送分支 + 标签 发布行为: - 匹配 `v*` 的标签推送触发 `.github/workflows/publish.yml` - 工作流构建/测试,验证标签版本与 `package.json` 匹配,发布到 npm,并创建 GitHub Release - 预发布版本(例如 `1.2.0-beta.1`)使用 npm dist-tag `next` 发布 一次性设置: 1. 在 npm 中,为此 GitHub 仓库和工作流文件配置 Trusted Publisher 2. 在 GitHub 中,创建环境 `npm-release` 并可选地要求审查者/限制标签 3. 使用 GitHub ruleset 保护发布标签(例如 `v*`),以便只有维护者可以创建它们 ## 安全性 - 使用 TypeScript Language Service 进行重命名(而不是正则表达式/文本替换) - 验证输出在重命名后可以编译 - 从不修改来自 `node_modules` 或 `.d.ts` 文件的符号 - 跳过带有装饰器的符号(装饰器名称反射会破坏) - 通过三层诊断检测动态属性访问: - **Error**: 使用与前缀属性匹配的字符串字面量类型的括号访问(已证实会损坏) - **Warn**: 使用宽泛 `string` 或 `any` 类型的括号访问(可能会损坏) - **Silent**: 数组/元组索引(安全,已抑制) - 跳过已经以 `_` 开头的符号 ## 要求 - Node.js >= 18 - TypeScript >= 5.0(作为项目依赖) ## 许可证 MIT
标签:AST操作, CMS安全, DNS 反向解析, esbuild, JavaScript, MITM代理, Terser, TypeScript, 代码优化, 代码压缩, 代码混淆, 体积优化, 内部符号, 前端工程化, 安全插件, 属性混淆, 文档结构分析, 符号重命名, 编译器API, 自动化攻击, 自动化攻击