mcpField
G
gdp-ts

gdp-ts

github.com/rauchg安全847116 次安装3 周前更新
代码仓库

关于 gdp-ts

gdp-ts 是一个 TypeScript 库及工作流,将授权检查绑定到敏感函数实际使用的值。它通过命名值和不可伪造的证明类型,让缺失或不匹配的检查成为编译错误,并提供额外的 lint 规则。

主要功能

  1. 01将授权证明绑定到命名值
  2. 02要求敏感函数签名包含证明
  3. 03将证明创建限制在可信模块
  4. 04提供 ESLint 和 Oxlint 规则
  5. 05支持编译期回归检查

适用场景

  1. 01防止授权检查对应到错误资源
  2. 02将缺失授权凭据变为编译错误
  3. 03在 TypeScript 代码库中加入基于证明的访问控制

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rauchg/gdp-ts --skill gdp-ts

安装后在 Claude Code 中输入/gdp-ts确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
errors.md
limits.md
patterns.md
recipe.md
where-to-stop.md

详细介绍

gdp-ts

敏感函数(只能由部分调用方读取或更改某些内容的函数)需要接收针对其确切参数的证明,因此未经过检查就调用它,或使用针对其他值的检查,都无法通过编译。

  1. 为值命名。 name(viewer.id, projectId, async (user, project) => ...) 会为每个值赋予一个仅在编译时存在的名称,该名称在回调中有效。
  2. 证明名称对应的事实。 proofs/ 中的一个小型可信模块会运行检查并返回证明,例如 UserIsProjectAdmin<U, P>,或 null。只有该模块能创建证明。
  3. 要求提供证明。 敏感函数接收 project: Named<P, ProjectId> 和 _proof: UserIsProjectAdmin<U, P>。错误的证明、针对另一个值的证明、原始 ID 或未提供证明,都会导致编译错误。

这不是定理证明器。只证明那些缺失时会导致事故的事实,然后就此为止(何时停止)。

工作流

在使用此模式的代码库中添加或更改授权逻辑时:

  1. ID 是否已添加品牌类型?如果没有,先添加。
  2. proofs/ 中是否已有你需要的事实对应的证明?如果有,复用它。如果没有,添加一个文件:私有的 defineProof、导出的 interface X<...> extends Proof<"X", [...]> {},以及导出的 async function x(...named): Promise<X<...> | null>。不要添加其他内容。
  3. 敏感函数是否接收针对其已命名参数的证明?如果它接收原始 ID,就更改签名;让编译器列出需要修复的调用位置。
  4. 请求是否携带操作会信任的其他值(Cookie 中的令牌、请求正文中的 ID)?在可信模块中,针对这些值声称可以解锁的资源进行检查(TokenUnlocksUrl<H>)。跨资源和跨租户漏洞就容易出现在这里。
  5. 是否存在非用户相关的前置条件(套餐、范围、状态)?也将它们设为证明(PlanIncludesPasswordProtection<P>)。
  6. 在处理程序主体中(而非中间件中):调用 name(...),调用策略函数,将 null 转换为适当的 HTTP 响应,并在回调中完成所有敏感操作。如果这段代码很可能会在许多地方调用,可以考虑将其移到共享函数中(步骤 5:在处理程序中命名并证明,将 null 转换为响应)。
  7. 切勿为了满足证明或 Named 类型而写 as。如果你觉得有此必要,说明签名不正确,或者你不在 proofs/ 中。如果代码库还没有 gdp-ts lint 预设,就添加它(步骤 6);如果已有,就修复 lint 错误,而不是禁用规则。
  8. 切勿导出证明器,切勿在 proofs/ 之外调用 defineProof,也切勿从 name() 回调中返回 Named,或提及该回调中名称的证明。
  9. 将你差点犯下的新错误添加到 mistakes.ts 中(这是一个经过类型检查但永不运行的文件),作为 // @ts-expect-error 下的一行代码。这是你能编写的成本最低的回归测试(详情)。
  10. 测试你添加的证明函数。它是唯一可能在运行时出错的部分。

操作指南完整展示了每个步骤。

参考资料

阅读你需要的部分:

文件阅读时机
references/recipe.md添加证明、策略或敏感函数,或采用此模式时。分为六个步骤:为 ID 添加品牌类型、可信模块、将策略表示为联合类型、敏感函数、在处理程序中命名、lint 规则。
references/patterns.md复用证明、由更强证明推出较弱证明、携带证据的证明、与用户无关的前置条件。
references/errors.md类型错误中提到 Named<...>、证明类型或“两个具有此名称的不同类型”时。
references/limits.md判断这些类型能保证和不能保证什么(伪造、过时的证明、较旧版本的 TypeScript)。
references/where-to-stop.md决定需要证明到什么程度时。

API

所有导出内容均来自 @gdp-ts/core。

导出项说明
Named<N, A>一个类型为 A、具有编译时名称 N 的值。通过 .value 读取。N 是不变的。
name(a, k) / name(a, b, k) / name(a, b, c, k)为一个到三个值赋予新名称,作用域限定为 k。返回 k 的返回值(包括 Promise)。
Proof<Kind, About>证明事实 Kind 对名称元组 About 成立的证据。具有运行时属性 kind: Kind。两个参数都是不变的。
defineProof(kind)返回一个 Prover:{ kind, prove(...named) }。prove 根据其参数推断 About。请将其设为执行检查的模块私有内容。
NameOf<T>Named<N, A> 中的 N。很少需要使用。
NamesOf<S>Named 值元组中的名称。用于 prove 的签名。

针对类型系统无法弥补的缺口,可使用 lint 预设(参见配方步骤 6):

导入项说明
@gdp-ts/core/lint/eslintgdp(options?):ESLint 扁平配置项。放在 typescript-eslint 配置之后展开。
@gdp-ts/core/lint/oxlintgdp(options?):Oxlint 配置(支持 TypeScript 7)。
@gdp-ts/core/lint/plugin规则本身(gdp-ts/no-define-proof、no-exported-prover、no-proof-assertion、no-type-assertion、no-any),适用于自定义配置。

选项:proofs(受信任模块的 glob 模式,默认值为 **/proofs/**)、strict (同时禁止在这些模块之外使用所有 as 和 any,默认值为 false)、 allowAssertions(严格模式的例外项,默认值为 **/lib/ids.ts)、 proofImports 和 files。

库中唯一断言 proof 类型的地方是 prove 内部,因此使用它的代码无需进行断言。运行时开销为每次调用 defineProof 创建一个冻结的 { kind } 对象; prove 每次都会返回同一个对象,而 name 会将每个值包装在一个冻结的 { value } 中。

评论

支持 Markdown