mcpField
C
convex-authz

convex-authz

github.com/get-convexセキュリティ6931.4k 回インストール1 日前に更新
リポジトリ

convex-authz について

convex-authzは、Convexバックエンドの認可監査と強化を行うスキルです。IDやデータ所有権、機密データへのアクセスを扱う開発者向けに、4種類の問題をスキャンし、既定の方法で修正して型チェックします。

主な機能

  1. 014種類の認可問題をスキャン
  2. 02認証基盤の有無を確認
  3. 03本人確認と所有権チェックを適用
  4. 04親リソースへの書き込み権限を監査
  5. 05修正後に型チェックと再スキャン
  6. 06問題の種類ごとに変更を報告

活用シーン

  1. 01クライアント指定のIDによるなりすましリスクの検出
  2. 02読み書き時の所有権チェックの確認
  3. 03機密データのアカウント間漏えいの調査
  4. 04子レコード書き込み前の親リソース確認

インストール

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

$npx skills add https://github.com/get-convex/agent-skills --skill convex-authz

インストール後、Claude Code で/convex-authzを入力して読み込まれたか確認します。

詳細

Convex 認可の監査/強化

一般的なレビューではなく、認可に特化したスペシャリストです。生成された Convex バックエンドを対象に測定した、実際の不具合の最大クラスターを占める 4 つのパターン(引数由来の ID による本人確認 25 件 + 所有権チェック漏れ 13 件 + 引数による PII 漏えい 6 件 = 確認済み不具合 214 件中 44 件)を検出して修正します。さらに、フィクスチャ測定で 3 パターンのスキャンでは見逃すことが示された、所有権パターンの書き込み時における親参照のバリエーションも対象とします。まず、決定論的なスキャン(客観的な正規表現ベース)を実行し、次に検出したすべての箇所に convex-expert.md で定義されている標準の requireIdentity/requireOwner 強化パターンを適用し、その後 tsc で検証します。このパターンを再導出するのではなく、プラットフォームの標準的な修正としてすでに文書化されているものを適用します。

ワークフロー

  1. 最初に必ず行う手順 — ctx.auth による強制チェックを追加する前に、認証基盤が存在するか確認します:(1)プロバイダーを含む auth.config.ts はありますか?(2)認証サブジェクト(tokenIdentifier/identity.subject)をキーとする users/identities テーブルはありますか?どちらか一方でも欠けている場合は、requireIdentity/requireOwner を追加してはいけません。認証基盤のないアプリでは、ctx.auth.getUserIdentity() は常に null を返します(強制チェックは機能せず、呼び出しのたびに 401 になるか、さらに悪い場合はチェックが回避されたり、メールアドレス文字列のようなサブジェクトではないフィールドと誤って比較されたりします)。その結果、レビュー担当者に修正ではなく新たな認可不備として正しく指摘されます。認証基盤のないアプリでは、代わりに次の対応を行います:(a)特権操作/管理操作については、公開クエリ/ミューテーションを internalQuery/internalMutation に変更します(公開経路を完全に削除でき、安全で、認証基盤も ctx.auth も必要ありません)。また、(b)ユーザーに次のように伝えます:’このアプリには認証基盤がありません。ユーザーごとの所有権チェックを追加するには、まず /add auth または認証セットアップを実行してから、convex-authz を再実行してください。’ 認証基盤のないアプリでは、この internal 化と保留の対応を除き、以下の手順 1〜3 を公開関数に対して実行してはいけません。認証基盤(auth.config.ts とサブジェクトをキーとする users テーブルの両方)が存在する場合に限り、手順 1〜3 で requireIdentity/requireOwner の追加に進みます。
  2. スキャン(決定的で、客観的な確認を優先):convex/**/*.ts の各ファイルを対象に(convex/_generated/ と .d.ts は除外)、次の 4 つのパターンを grep します: (a) 引数による ID の偽装:公開の query(/mutation( オブジェクトの args ブロックで、userId/actorId/ownerId/authorId/accountId が v.id(...) 型として宣言されており、その関数全体のブロック(args + handler)に ctx.auth への参照がないもの。正規表現:/\\b(userId|actorId|ownerId|authorId|accountId)\\s*:\\s*v\\.id\\(/ が args: { ... } ブロック内にあり、対応する外側の (query|mutation)\\(\\s*\\{ ... } ブロック内に /\\bctx\\.auth\\b/ が存在しないもの(単語境界により、構造上 internalQuery/internalMutation は除外されます)。 (b) 所有権チェックの欠落:公開の query(/mutation( で、ハンドラーが ctx.db.get(args.<xId>)(_id 型の引数)でドキュメントを読み込んだ後、同じ ID に対して ctx.db.patch/ctx.db.delete/ctx.db.replace を呼び出すか、そのドキュメントのフィールドを直接返しており、ブロック内のどこにも <doc>.<ownerField> を認証 ID と比較する処理(identity.subject または ctx.auth から取得した値を使った ===/!==)がないもの。 (c) PII が漏えいする公開クエリ:公開の query( で、returns(または返される生のドキュメント)に機微情報と思われるフィールド(email、revenue、ssn、password、token、auditLog、dashboard 形式の集計情報)が含まれ、クライアントが指定した ID をパラメーターに取り、その ID が呼び出し元自身のスコープに属することを確認する ctx.auth チェックでアクセスを制限していないもの。 (d) 書き込み時の親参照に対する所有権チェック:公開の mutation( で、引数に親/コンテナテーブルの v.id(...)(projectId、boardId、teamId、orgId、listId、folderId、conversationId、accountId など)が含まれ、ハンドラーがそれを ctx.db.insert/ctx.db.patch の外部キーとして使用し、該当する親ドキュメントの所有者(またはメンバー)が呼び出し元であることを確認せずに、子行をそのコンテナに追加または移動しているもの。他者のコンテナ内に行を作成することは、他者の行を変更することと同じ不備です。呼び出し元が誰かを特定する修正(パターン a)だけでは、書き込み先として許可された場所のチェックにはなりません。パターン a〜c の処理後、すべての公開ミューテーションで残っている v.id(...) 引数を再監査し、このパターンに該当するものがないか確認してください。パターン a の修正後も、親 ID 引数が未チェックのまま残ることがよくあります。 該当するすべての箇所について、ファイル、行番号、および一致した 4 つのパターンのいずれかを報告してください。これは客観的で、モデルに依存しないベースラインです。判断に直行して、この確認を省略してはいけません。
  3. 強化(認証基盤のあるアプリのみ — 手順 0 を参照):該当箇所ごとに content/convex-expert.md の標準パターンをそのまま適用します。新しいヘルパーを独自に考案してはいけません。存在しない場合は、requireIdentity(ctx)(ctx.auth.getUserIdentity() が null なら 401 をスローし、ID を返す)と requireOwner(ctx, doc)(doc が null なら 404、doc.ownerId !== identity.subject なら 403 をスローし、それ以外は doc を返す)をエクスポートする convex/model/auth.ts を追加します。各該当関数を書き換えます。クライアントから渡された ID 引数を requireIdentity(ctx) に置き換えます。_id をキーにした読み取り/変更では、行に触れる前に requireOwner(ctx, await ctx.db.get(args.xId)) を適用します。PII を返す各クエリでは、呼び出し元自身のスコープ外を読み取る前に requireIdentity/requireOwner(または明示的なスタッフ/ロールチェック)でアクセス範囲を制限します。パターン (d) に該当する場合は、子行を挿入/パッチする前に、参照先の親ドキュメントを読み込んで requireOwner(ctx, parent) を適用します(または、コンテナがメンバーを配列で表す場合は、スキーマに定義されたメンバーシップチェックを使います。例:participantIds.includes(user._id))。スキーマ上、所有権が生のサブジェクトではなく users 行の ID で管理されている場合は、まずサブジェクトをキーとするインデックスを使って呼び出し元の users 行を取得し、user._id と比較します。Id<"users"> フィールドと identity.subject の比較は一致せず、強制チェックがひそかに機能しなくなります。スコープを決して広げてはいけません。正当な理由で任意のユーザーに対して処理を行う内部/管理関数は、公開関数にせず、internalQuery/internalMutation のままにします。該当対象として扱わず、変更もしません。
  4. 検証:編集後に npx tsc --noEmit(またはプロジェクトの型チェック用スクリプト)を実行します。型チェックに通らない強化作業は完了ではありません。その後、手順 1 のスキャンを再実行し、該当が 0 件であることを確認します(ctx.auth がブロック内に現れ、所有権の比較が追加されるため、修正済みのパターンは正規表現に一致しなくなります)。
  5. 4 つのルールのパターンごとにファイル:行番号をまとめて報告し、それぞれがどのように悪用されるか(誰が誰になりすませるか/誰のデータを読めるか)を説明し、適用した具体的な差分を示してください(認証基盤のないアプリの場合は、internal 化と保留の差分、および認証セットアップを促す案内を示してください)。修正内容を文章で説明するだけではいけません。

ルール

  • 必須の最初の手順:requireIdentity/requireOwner を追加する前に、認証の基盤が存在することを確認してください。provider を含む auth.config.ts と、認証サブジェクトをキーとする users/identities テーブルが必要です。どちらかが欠けている場合、ctx.auth に基づく認可を追加しないでください(機能しないか、認証情報との不整合が生じ、新たな認可不備を招きます)。代わりに、該当する公開管理者向け/特権関数を internalQuery/internalMutation に変換し、まず認証を設定してから convex-authz を再実行するようユーザーに伝えてください。
  • 判定の前に客観的にスキャンしてください。まず4つの決定的な grep を実行してください。LLM による判断へ直行せず、スキャン結果に問題がなくても、内部関数/管理者向けの例外を目視で確認してください。
  • ID は常に ctx.auth から取得し、クライアントが指定した引数から取得してはいけません。正当な例外は、公開されることのない internalQuery/internalMutation/internalAction のみです。
  • _id 引数をキーとする読み取りや変更では、行に触れる前にサーバー側で所有権を確認してください(requireOwner または同等のインライン比較を使用)。ログイン済みであることは、その行を所有していることと同じではありません。
  • 公開 mutation が挿入時または行の移動時に外部キーとして使用する v.id(...) 引数については、呼び出し元に対して、参照先の親に対する所有権(またはメンバーシップ)を事前に確認してください。他人のプロジェクト/ボード/アカウントに子行を作成することは、その人の行を変更するのと同じ不備です。この不備は、ID を引数から取得する問題を修正しても残り、別途確認が必要です。
  • 未認証クライアント、またはクライアントが指定した別アカウントの ID を使うクライアントから、PII/財務/監査データを返す公開 query にアクセスできる状態を決して残さないでください。
  • content/convex-expert.md にある requireIdentity/requireOwner をそのまま再利用してください。別のヘルパーを作成したり、新しいエラーの意味を定義したりしないでください。
  • 強化後は必ず tsc で検証してください。型チェックに通らない修正は出荷しないでください。
  • これは対象を絞った認可チェックであり、一般的なコードレビューではありません。パフォーマンス/スキーマ/バリデーターの指摘に範囲を広げず、それらは convex-reviewer に引き継いでください。
  • プロジェクトに convex/ ディレクトリがない場合は、完全にスキップしてください。

コメント