mcpField
G
gdp-ts

gdp-ts

github.com/rauchgセキュリティ847116 回インストール3 週間前に更新
リポジトリ

gdp-ts について

gdp-ts は、認可チェックを機密関数が扱う正確な値に結び付ける TypeScript ライブラリとワークフローです。名前付きの値と偽造できない証明型により、チェックの欠落や不一致をコンパイル時エラーにし、追加の lint ルールも提供します。

主な機能

  1. 01認可証明を名前付きの値に結び付け
  2. 02機密関数のシグネチャで証明を必須化
  3. 03証明の作成を信頼されたモジュールに限定
  4. 04ESLint と Oxlint のルールを提供
  5. 05コンパイル時の回帰チェックを支援

活用シーン

  1. 01認可チェックが別のリソースに適用されるのを防止
  2. 02認可証明の欠落をコンパイル時エラー化
  3. 03TypeScript コードベースに証明ベースのアクセス制御を導入

インストール

コマンドをコピーしてターミナルで実行します。

$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/ に必要な事実を証明するものがありますか?ある場合は再利用します。なければ、ファイルを 1 つ追加します。内容は、非公開の 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)。
  7. 証明や Named 型を満たすために as を絶対に使わないでください。必要だと感じる場合は、シグネチャが間違っているか、proofs/ の外にいます。コードベースに gdp-ts の lint プリセットがまだない場合は追加します(レシピのステップ 6)。すでにある場合は、無効化するのではなく lint エラーを修正します。
  8. 証明関数をエクスポートしないでください。proofs/ の外で defineProof を呼び出さないでください。name() コールバックから Named を返したり、そのコールバックの名前に言及する証明を返したりしないでください。
  9. もう少しで犯しそうになった新たな誤りを、mistakes.ts(実行されない、型チェック用のファイル)の // @ts-expect-error の下に 1 行で追加します。これは作成できる最も低コストの回帰テストです(詳細)。
  10. 追加した証明関数をテストします。実行時に誤りがあり得るのはそれだけです。

レシピでは、各ステップを詳しく説明しています。

参考資料

必要なものを読んでください。

ファイル読むタイミング
references/recipe.md証明、ポリシー、機密性の高い関数を追加するとき、またはこのパターンを導入するとき。6 つのステップ:ID のブランド型化、信頼できるモジュール、共用体としてのポリシー、機密性の高い関数、ハンドラー内での命名、lint ルール。
references/patterns.md証明の再利用、強い証明から弱い証明を導く方法、証拠を保持する証明、ユーザーに関するものではない前提条件について。
references/errors.md型エラーに Named<...>、証明型、または「この名前の型が 2 つあります」と表示される場合。
references/limits.md型が保証することと保証しないこと(偽造、古い証明、古い TypeScript)の判断について。
references/where-to-stop.mdどこまで証明するかを決めるとき。

API

すべて @gdp-ts/core からエクスポートされます。

エクスポート内容
Named<N, A>コンパイル時の名前 N を持つ型 A の値です。.value で読み取れます。N は不変です。
name(a, k) / name(a, b, k) / name(a, b, c, k)1〜3 個の値に、スコープを k とする新しい名前を付けます。k の戻り値(Promise を含む)を返します。
Proof<Kind, About>名前のタプル About について、事実 Kind が成り立つことを示す証拠です。実行時には kind: Kind を持ちます。両方の型パラメーターは不変です。
defineProof(kind)Prover({ kind, prove(...named) })を返します。prove は引数から About を推論します。チェックを行うモジュール内でのみ使用してください。
NameOf<T>Named<N, A> の N です。必要になることはほとんどありません。
NamesOf<S>Named 値のタプルの名前です。prove のシグネチャで使用されます。

型システムでは防げない抜け穴に対応するリントプリセット(レシピのステップ 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(strict モードの例外。デフォルトは **/lib/ids.ts)、 proofImports、files。

このライブラリで証明型をアサートする箇所は prove の内部だけなので、これを使うコードでアサーションは不要です。実行時のフットプリントは、defineProof の呼び出しごとに 1 つ作られる凍結済みの { kind } オブジェクトです。 prove は毎回同じオブジェクトを返し、name は各値を凍結済みの { value } でラップします。

コメント