cra-check について
cra-checkは、公開GitHubリポジトリについて、EUサイバーレジリエンス法(CRA)に関連する8つのセキュリティシグナルを確認します。公開情報に基づく目安であり、適合性審査ではありません。
主な機能
- 01CRA関連の8項目を確認
- 02リポジトリのファイルとGitHub情報を取得
- 03シグナルの可視性と確認元を表示
- 04判定基準と未測定範囲を提示
活用シーン
- 01公開リポジトリのセキュリティ対策を概観
- 02公開情報で確認できない項目を把握
設定
リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。
https://api.tokenizen.nl/mcpclaude mcp add --transport http holistis-cra-check https://api.tokenizen.nl/mcp{"mcpServers":{"holistis-cra-check":{"type":"http","url":"https://api.tokenizen.nl/mcp"}}}Cursor で開く{"servers":{"holistis-cra-check":{"type":"http","url":"https://api.tokenizen.nl/mcp"}}}VS Code で開く{"mcpServers":{"holistis-cra-check":{"type":"http","url":"https://api.tokenizen.nl/mcp"}}}{"mcpServers":{"holistis-cra-check":{"serverUrl":"https://api.tokenizen.nl/mcp"}}}コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。
ツール
詳細
self-check-server
tokenizen.nl/check の背後で動作する、小規模な HTTP バックエンドです。ブラウザーでは node:dns や node:crypto を直接実行できないため、ブラウザーページが代わりに呼び出す、薄い実処理(モックではありません)のレイヤーです。所有権証明のロジックを再実装するのではなく、すでにテスト済みの endpoint-attest と trust-attest-server の関数を HTTP 経由で呼び出せるようにするだけです。
独立したパッケージとして存在する理由
endpoint-attest の verifyOwnership() と trust-attest-server の getOwnershipChallenge() は、呼び出し元のエージェントが自身の運用者のエンドポイントに対して動作する MCP ツール呼び出し用に作られました。同じ関数を匿名の公開ウェブフォームの背後に置くと、脅威モデルが変わります。訪問者は誰でも任意の http(s) URL を送信でき、このサーバーに対して実際の DNS ルックアップや HTTP フェッチを行わせることができます。これは、これまで公開されていなかったこれらの関数にとって、典型的な SSRF の攻撃対象領域です。そのため、ここには次の 2 つが用意されています。
tokenizen-net-guard(packages/net-guard)— 外部への DNS または HTTP 呼び出しは、すべてこれを経由します。ホストは一度だけ解決され、返された各アドレスがループバック、プライベート、リンクローカル(クラウドメタデータアドレスを含む)、予約済みの各範囲に該当しないか確認されます。そのうえで、リダイレクトを含むすべてのホップで、検証済みのアドレスにのみ接続します。この固定化により、このパッケージの初期バージョンで明示的に認めていた DNS リバインディングの隙を塞いでいます。ビルドテスト(net-guard/src/no-raw-egress.test.ts)では、ここにある本番ファイルが生の fetch や DNS 呼び出しにアクセスすると失敗します。src/rate-limit.ts— この API は設計上、認証なしで利用でき、リクエストごとに実際のネットワーク I/O が発生するため、両方のルートに対して IP ごとの最小限のインメモリ固定ウィンドウ方式のレート制限を設けています。単一プロセスでのみ動作します。複数インスタンスでデプロイする場合は、代わりに共有ストアが必要です。
trust-attest-server の無料/有料の区分に対応する 2 つのルート
POST /api/challenge(無料、ネットワーク呼び出しなし)—getOwnershipChallengeをラップします。本文:{ endpoint, ownerAddress }。POST /api/verify-ownership(無料、実際の DNS/HTTP チェックを 1 回実行)—verifyOwnershipを直接ラップします。本文:{ endpoint, ownerAddress, method }。これは所有権の証明のみを行います。チェックを実行して証明書に署名する有料ツールrequestTrustAttestationは呼び出しません。公開ページの無料の「検証」ボタンが有料アクションを決して実行しないようにするためです。有料ステップを実際の支払い(x402/mcp-paywall)の背後につなぐのは、別途行う後続の変更です。
どちらのルートも、デフォルトでは trust-attest-server のモジュールレベルのアテスター ID を共有するため、このサーバーが発行したチャレンジは検証時の期待値と一致します。これは attester-identity.ts にすでに記載されている、「プロセスの再起動でリセットされる一時的な ID」という注意点と同じです。
無料のリアルタイム x402 チェック
POST /api/x402/check(無料、訪問者自身のエンドポイントを1回測定)— リクエストボディ{ url, method? }。tokenizen.nl/x402-check の背後で提供されます。サーバーは匿名のリクエストをエンドポイントに1回送信します。決して支払いは行わず、測定日付きの判定を返します。公式の x402 購入者スキーマ(@x402/core のparsePaymentRequired。Payment-Required ヘッダーまたはボディから読み取ります)を支払いリクエストが通過するかどうか、また agent-receipt-verify のレシート検証レベル(支払い不要のレベル 0~2)のどこまで到達するかを示します。エンドポイントの生のヘッダーとボディは返されません。
これは、当サーバーが自分のアドレスから他のホストを調査するために使われないよう、ライブ測定を一切行わないという src/x402-badge.ts に記されたルールを変更するものです。バッジでは引き続きそのルールを適用します。ライブのエンドポイントでは、src/x402-check.ts の制限によって「一切行わない」を置き換えます。HTTPS とポート 443 のみ、公開ドメイン名のみ(IP リテラルは不可)、GET と POST のみで、固定の {} ボディを使用します。リダイレクトは最大 2 回で、貼り付けられたホストまたはその www サブドメインにのみ留まる場合に限ります。レスポンスの上限は 64 KB(DID ドキュメントは 8 KB)、測定ごとの外部リクエストは最大 6 件、タイムアウトは 45 秒で、期限を過ぎるとソケットを中断します。同時に実行できるチェックは最大 8 件、結果のキャッシュ期間は 2 分、1 ホストあたりのライブ測定は 1 時間に最大 12 件、全体では 200 件です。さらに、1 IP あたり 10 分間に 8 リクエストまでに制限します。貼り付けられたホスト以外にリクエストできるのは、クエリなしの固定ファイル /.well-known/did.json だけです。これは、測定対象の当事者がオファーで指定する did:web ドキュメントを選ぶためです。結果は保存されません。キャッシュはメモリ上にあり、サーバーは URL をログに記録しません。回答ごとに1行のカウント情報(ルート、ステータス、自身のクライアントかどうか)を記録します。src/usage-log.ts を参照してください。
ライブラリには、did:web のキー検索に注入された fetch を渡す最初の公開バージョンである agent-receipt-verify の 0.2.9 を正確に使用します。これより古いバージョンは使用しないでください。この修正以前は、測定対象の当事者がホストを選べるキー検索でグローバルの fetch が使われ、上記のガードがすべて迂回されていました(agent-receipt-verify#46)。この問題が再発した場合、x402-check.test.ts が失敗します。
既知の制限について、敵対的レビュー(独立したレビューではなく、私たち自身によるもの)で見つかり、コードでは解消されていないため、ここに記します。多数の IP アドレスを管理する者が、1 時間あたりの全体予算を使い切ることができます。その後、リセットされるまで、すべての訪問者に 429 が返されます。また、同一ホストに異なるクエリ文字列で 12 回チェックすると、そのホストの所有者は 1 時間チェックできなくなります。DNS ルックアップを応答しないネームサーバーに向けるホスト名は、共有プールのスレッドを占有し続けます。同時実行数の上限を 8 件にすることで影響を抑えています。また、サービス環境で UV_THREADPOOL_SIZE=16 を設定すると余裕ができます。プロキシの背後で運用する場合は、誰もポートに直接アクセスして独自の X-Forwarded-For を送信できないよう、HOST=127.0.0.1 を設定してください(本番サーバーにも、22、80、443 のみを開放するファイアウォールがあります)。
購入者チェックで x402-lint を使用しない理由:x402-lint はレスポンスボディのみを読み取りますが、v2 の販売者は要件を Payment-Required ヘッダーに入れ、ボディを {} にします。2026-10-24 に、当社のライブ有料ルートを使って測定したところ、「x402Version: Invalid discriminator value」と報告されました。同じスキーマでも、読み取る場所が異なります。
無料の CRA 準備状況チェック
POST /api/cra/check(無料。GitHubへの小規模な読み取り専用リクエストを約19回まで、そのうちAPIへのリクエストは最大6回)- 本文{ repo }。tokenizen.nl/cra-check の背後で提供されます。公開されている GitHub リポジトリについて、日付付きの「選定された8つのシグナル」の一覧を返します。各シグナルの状態はvisible、not_visible、not_measuredのいずれかです。対象となるシグナルは、連絡先メールアドレスまたは報告用リンクを明記したセキュリティポリシー(リポジトリのルート、.github/ または docs/ にある SECURITY.md、あるいは所有者の .github リポジトリにあるもの)、ソフトウェア部品表、署名付きリリースまたは来歴情報、ロックファイル、Dependabot または Renovate、CI でのセキュリティスキャン、過去90日以内のメンテナンス(デフォルトブランチへの最終コミット)、ライセンスです。表示されていないシグナルには、それを表示するために必要な内容が記載されます。また、確認対象となった GitHub ページと、それぞれの結果(sources:読み取り済み、一部読み取り済み、見つからず、移動済み、失敗)も一覧表示されるため、読者は同じページを開いて確認できます。
8つのシグナルの内容と、対象外の内容。 これらは、規則 (EU) 2024/2847 の附属書 I 第 II 部にある8つの法的要件(第1項から第8項)にそれぞれ対応するシグナルではありません。一部はそれらの要件に関係します(部品表は第1項、自動更新は第2項、セキュリティスキャンは第3項、脆弱性開示ポリシーと連絡先は第5項および第6項、署名付きリリースは第7項)。一方、依存関係の固定、最近のメンテナンス、ライセンスなど、同規則に明記されていない一般的な優良事例も含まれています。また、修正済みの脆弱性が公開されているか、脆弱性に実際に遅滞なく対処しているか、報告プロセス、文書など、複数の法的要件は測定していません。各トピックは2026-10-28に公式文書と照合しましたが、シグナルの選定とその解釈は私たち独自のものであり、弁護士による確認は受けていません。回答にはこれらすべてが明記されており(rules.basis、notice、whatThisDoesNotMeasure)、ルールセットのバージョンも含まれているため、ルールが変更されても日付付きの結果を意味のあるものとして保てます。これは公開データに基づく目安であり、コンプライアンスチェックではありません。
コードを形作るルール:確認可能な情報をすべて読み取った場合にのみ、シグナルを「not visible」とします。 ファイルツリー全体は、1回の呼び出し(再帰的な git trees)で取得するため、モノレポ内のどこにあるロックファイルや SBOM も検出できます。GitHub によってツリーが途中で切り詰められた場合、またはツリーが上限を超えた場合は、ファイルベースのシグナルを「not measured」とします。呼び出しに失敗した場合、ワークフローファイルを一部しか読み取れなかった場合(最大12個。リリース、公開、スキャン関連のファイルを優先し、各ファイルは64 KBまで)、または SECURITY.md を一部しか調べられなかった場合(16 KBを超えた場合、1000文字を超える行がある場合、または読み取り上限を超えた場合)も、「not measured」となります。リポジトリを読み取れない場合は502を返し、キャッシュには保存しません。未読み取りの部分があるレポートは、1時間ではなく5分間キャッシュします。リンクは、報告窓口らしいもの(security、advisories、report、disclosure、バウンティプラットフォーム、contact を含むもの)である場合にのみ連絡先として扱い、プロジェクトのホームページは対象外です。「not visible」を「missing」と表現することはありません。別のビルドサーバーやレジストリで行われた署名や SBOM の作成、リポジトリの設定で有効にされたスキャンや Dependabot アラートは、リポジトリからは確認できないためです。この方針は外部からのセカンドオピニオンと私たち自身による2回のレビュー(2026年10月28日)を経て定まりました。cra-repo-check.test.ts ですべてのケースを網羅しています。
悪用を防ぐための対策は、すべて src/cra-check.ts と src/cra-repo-check.ts にあります。訪問者が選べるのは owner/repo の組み合わせで、ホストは選べません。また、アドレスを組み立てる前に厳格なパターンに一致する必要があります(すべてのパスセグメントは再度パーセントエンコードされ、ファイルは HEAD から読み込まれるため、ブランチ名など、リポジトリ所有者が選んだテキストがアドレスに含まれることはありません)。問い合わせるホストは api.github.com と raw.githubusercontent.com のみで、net-guard の guardedFetch を通し、リダイレクトは追跡しません。すべてのレスポンスはバイト数に上限を設けて読み込みます(ファイルツリーは 3 MB、その他の API の応答は 1 MB、raw ファイルは 1 ファイルあたり 200 KB)。リポジトリ所有者がファイルのサイズを決められるためです。ポリシーテキストは 16 KB で切り詰めて、上限付きのパターンで行ごとに照合し、ワークフローテキストは 64 KB で切り詰めます。同時に処理するチェックは最大 6 件、チェックごとの期限は 30 秒で、期限を過ぎるとソケットを中断します。リポジトリごとのキャッシュは 1 時間(リポジトリは分単位では変化しないため)、「見つからない」場合のキャッシュは 5 分です。さらに、サーバー全体の時間あたりの予算(トークンなしでは 5 件、トークンありでは 250 件。存在しない名前は枠が返却され、4 倍の別予算から消費されます)と、これに加えて IP ごとの 10 分あたり 6 リクエストの制限があります。リポジトリ由来のテキストは切り詰めて制御文字を除去し、ページではテキストとして描画します。レポートは保存されません。応答は最大 1 時間サーバーのメモリに保持され、このサービスはリポジトリ名をログに記録しません。
GitHub の利用枠。 1 回のチェックで最大 6 回の API 呼び出しが発生します(リポジトリ、ファイルツリー、リリース、アドバイザリ、最後のコミット、およびリポジトリ独自のポリシーがない場合は所有者の .github ツリー)。raw ファイルの読み込みは GitHub の利用枠に含まれません。トークンがない場合、サーバー全体の利用枠は 1 時間あたり 60 回で、訪問者 1 人が全員分を使い切る可能性があるため、CRA_CHECK_GITHUB_TOKEN が設定されているか、匿名利用を有効にする CRA_CHECK_ALLOW_ANONYMOUS=1 が指定されていない限り、このルートは無効化されています(503)。このサービス専用に、権限なし(公開リポジトリ、読み取り専用)のファイングレインドトークンを作成し、CRA_CHECK_GITHUB_TOKEN に設定してください。利用枠は 5000 回に増え、同じトークンを他の用途に使う余裕も残ります(デフォルト設定ではその約半分までしか使いません)。また、トークンが送信されるのは api.github.com のみです。個人用トークンは絶対に使用しないでください。GitHub が「速度を落としてください」と応答した場合、応答は 429 となり、何もキャッシュされません。
既知の制限で、コードでは解消されていないもの:多数の IP アドレスを管理している人(トークンありの場合、約 40 個)は、新しい名前に対する予算を 1 時間使い切ることができます。その後、予算がリセットされるまで、新しい名前へのリクエストはすべて 429 になります。すでにキャッシュにあるリポジトリには引き続き応答します。CORS が決めるのは、どのブラウザーページが応答を読み取れるかだけであり、他の呼び出し元を止めるものではありません。名前が変更されたリポジトリは「移動しました」と応答し、現在の名前で再度問い合わせる必要があります。公開リポジトリや GitHub リリースの外部にあるポリシー、SBOM、署名ステップは確認できません。ルートより深い階層にある package.json の Renovate 設定は読み込みません。ライセンスの判定には、GitHub 独自の検出結果、またはルートにある LICENSE、COPYING、UNLICENSE ファイルを使用しますが、ファイルの内容は読み込みません。
このルートには、外部のセカンドオピニオン 1 件と、独自に行った敵対的レビュー 2 件(2026 年 10 月 28 日)が反映されています。2 回目のレビューで、SECURITY.md にある 200 KB の行によって正規表現の処理が約 20 秒間続き、すべてのルートが停止する可能性が見つかりました。現在、テストでは悪意のある入力の処理時間も検証しています。独立した外部レビューはまだ実施されていません。
使用方法
npm install # from the monorepo root
npm run build -w self-check-server -w endpoint-attest -w trust-attest-server
PORT=8787 npm run start -w self-check-server
# or, for local development with auto-reload:
npm run dev -w self-check-server
環境変数で設定します:PORT(デフォルトは 8787)、ALLOWED_ORIGINS(カンマ区切り。デフォルトは http://localhost:5173 と、本番環境の tokenizen.nl ドメイン)。
テスト
npm test -w self-check-server — すべて、実際の Express アプリを実際の(ループバック、エフェメラルポートの)ソケットで対象にし、DNS ルックアップと HTTP フェッチはすべて、ownership.ts/checks.ts がすでに使っているのと同じ依存性注入の仕組みを通じて注入したフェイクに置き換えています。このパッケージのテストでは、x402 チェックのテストも含め、実際の DNS クエリや外部への HTTP リクエストは一切行いません。x402 チェックのテストでは、egress を net-guard の動作を再現するフェイクに置き換えています。
ステータス
https://api.tokenizen.nl で稼働中です。手前に Caddy を置き、このサービスを専用の非特権ユーザーでその背後に配置し、手作業でデプロイしています(サービスユーザーとしてプルし、ビルドして、ユニットを再起動します)。所有権ルートと有料ルートは以前からあるもので、x402 チェックは新しいものです。x402 チェックは公開前に内部で敵対的レビューを受けました。これは自分たちによるレビューであり、独立したレビューではありません。また、このパッケージは外部の独立したレビューを受けていません。
MCP ツールとしての CRA チェック
同じチェックは POST /mcp(src/cra-mcp.ts)の MCP ツールとしても提供されているため、AI エージェントはブラウザーを使わずに実行を依頼できます。読み取り専用の check_github_repo ツールが一つあり、repo(owner/name または github.com の URL)を受け取り、POST /api/cra/check と同じ日付入りレポート、または簡潔なエラーを返します。同じチェッカーを通るため、ウェブページに適用される制限もそのまま適用されます。厳密な owner/repo パターン、1 時間のキャッシュ、一度に実行できるチェックは数件まで、そして GitHub の時間ごとの予算です。
ステートレスです。リクエストごとに専用のサーバーとトランスポートを用意し、リクエスト間で何も保持しません。POST のみ許可され(GET と DELETE は 405 を返します)、1 つのリクエストに多数のツール呼び出しを含められるため、メッセージのバッチは拒否されます。ツールを呼び出すリクエストにはウェブルートと同じレート制限(アドレスごとに 10 分間で 6 回)が適用されます。接続、ツール一覧の取得、ping にはより緩い制限(10 分間で 60 回)が適用されるため、エージェントはチェック回数を使い切らずに内容を確認できます。回答は、他の無料チェックと同様にカウントされます(src/usage-log.ts:ルート mcp、ステータス、自社クライアントのフラグ。アドレスとリポジトリ名は記録しません)。
MCP Inspector のデフォルトのプロキシモードで試してください:npx @modelcontextprotocol/inspector を実行し、トランスポートに Streamable HTTP、URL に https://api.tokenizen.nl/mcp を指定します(ブラウザーからの直接接続モードは CORS リストで許可されていません)。制限を超えたツール呼び出しには、HTTP 429 を単独で返すのではなく、isError と待ち時間(秒)を含むツール結果が返されます。使用回数としてカウントされるのはツール呼び出しのみです。このフォルダーの server.json は、公式 MCP レジストリへの登録情報です。
当社を介さずに Signal ログを検証する
website/public/verify-cra-log.mjs(https://tokenizen.nl/verify-cra-log.mjs で配信)は、ログをテキストだけで検証します。ファイル 1 つ、インストール不要、ネットワーク不要です。node verify-cra-log.mjs yourlog.jsonl を実行すると、チェーンの先頭とともに ok が表示されるか、理由とともに NOT OK at line N が表示されます。これは src/cra-log.ts の verifyChain と同じチェックです。src/cra-log-standalone.test.ts は正常なログと 14 種類の破損ログの両方でこの 2 つを実行し、結果が異なると失敗します。これにより各行が同じログに属し、改変されていないことが分かります。書き込まれた時刻は、先頭のタイムスタンプ(OpenTimestamps、ots verify)で確認できます。
サーバー上で手作業で行う週次実行
bench/cra-weekly.sh repos.txt logdir は、repos.txt にある各リポジトリについて 1 行を追加し、すべてのログの先頭を記録して、そのファイルに OpenTimestamps のタイムスタンプを付与します。数時間後に ots upgrade <file>.ots を実行すると、タイムスタンプが確定します。Linux サーバーにはクライアントが /opt/ots にあります(Python 仮想環境です。元に戻すにはフォルダーを削除します)。OTS=/opt/ots/bin/ots bench/cra-weekly.sh ... として実行します。クライアントは Windows では起動しません(ライブラリが不足しているため)。そのため、この実行はサーバー上で行います。ログは顧客データ(リポジトリ名)です。ログ用フォルダーはリポジトリの外に置き、サーバー外に持ち出すバックアップにも含めないでください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。