コミュニティパッケージ:
o1js-scanは公式の o1js Community Packages ディレクトリに掲載されています。
最新版: 0.22.0 — ローカル MCP サーバーにより、 Claude Code、Cursor などの AI コーディングアシスタントが zkApp を書いている間に スキャナーを実行できます:
pip install 'o1js-scan[mcp]'。お使いのマシン上で stdio 経由で動作し、何もアップロードしません。ファイルを一切解析しなかったスキャンは クリーンな結果ではなくエラーであり、所見のない結果はセキュリティ保証ではないと明示します。 コアスキャナーは依然として依存関係がありません。 CHANGELOG を参照してください。
以下に含まれる zk 回路の健全性バグ を検出する、高速で依存関係のない静的解析ツール:
.ts / .js) — @method 本体から生成される Kimchi 回路.nr) — Aztec の Rust ライクな ZK DSL (aztec-nr 形式のパターンを含む)セキュリティ上重要なバグは通常、証明システムにあるのではなく、
アプリケーション自身の制約 にあります。それは証明者が制御するものの、回路が決して束縛しない witness です。
o1js-scan は、Mina および Noir エコシステムにおける Circom のいとこたちのための、under-constrained-signal スキャナーです。
関連項目: gnark-safety (gnark 回路)、vk-guard (検証鍵のリグレッション)。
pip install o1js-scan
# or: pipx install o1js-scan
# or: npm install -D o1js-scan
o1js-scan path/to/zkapp # o1js + Noir (auto)
noir-scan path/to/circuits # same binary — Noir-friendly alias
noir-scan . --lang noir --fail-on high --sarif noir.sarif
withdraw の金額が、オンチェーン状態に一切束縛されない、prover が制御する witness である vault を考えます:
$ o1js-scan examples/vulnerable_vault.ts --include-examples
LOW O1JS_UNCONSTRAINED_RECIPIENT vulnerable_vault.ts:23 fn=withdraw Recipient `to` is prover-chosen in `withdraw`
HIGH O1JS_UNCONSTRAINED_WITNESS vulnerable_vault.ts:23 fn=withdraw Unconstrained witness `amount` flows to send_amount in `withdraw`
o1js-scan: 2 finding(s) [1 high, 1 low] in 1 of 1 file(s) — fails (--fail-on high)
$ echo $?
1
--include-examples が必要なのは、デモファイルが examples/ 配下にあり、パスクラシファイアがデフォルトでこれを格下げするためです。これにより、リポジトリ自身のサンプルコードがビルドを失敗させることがなくなります。src/ 内の同じコントラクトは、フラグなしで HIGH を報告します。
HIGH の検出結果は drainable なバグです。修正済みのコントラクト
(examples/safe_vault.ts) はこれを除去して 0 で終了し、prover が選択した受取人に関する情報提供目的の LOW のみを残します:
$ o1js-scan examples/safe_vault.ts --include-examples
LOW O1JS_UNCONSTRAINED_RECIPIENT safe_vault.ts:23 fn=withdraw Recipient `to` is prover-chosen in `withdraw`
o1js-scan: 1 finding(s) [1 low] in 1 of 1 file(s) — passes (--fail-on high)
$ echo $?
0
脆弱/修正済みのペアについては、o1js と Noir の examples/ を参照してください。
pip install o1js-scan
分離されたグローバルCLIインストールには、pipxを使用します:
pipx install o1js-scan
Node/npm ベースの Noir、Aztec、または o1js アプリケーションリポジトリの場合は、npm ラッパーをインストールします:
npm install -D o1js-scan
npx noir-scan . --lang noir --fail-on high
npm パッケージは同じ Python アナライザーの薄いラッパーであり、PATH 上の Python 3.8+(python3 または python)が必要です。特定のインタプリタを選択するには O1JS_SCAN_PYTHON を設定してください。
またはソースから:
git clone https://github.com/auditinfra-io/o1js-scan
cd o1js-scan
pip install -e .
サードパーティの Python 依存関係はありません。Python 3.8 以上。noir-scan コンソールスクリプトは o1js-scan と一緒にインストールされます(同じエントリポイント)。npm ラッパー経由でも同様です。
# scan a directory (recursively; skips node_modules, target/, .git, …)
o1js-scan path/to/project
# Noir-only / o1js-only
noir-scan circuits --lang noir
o1js-scan src --lang o1js
# scan a single file
o1js-scan src/MyContract.ts
noir-scan src/main.nr
# machine-readable output for CI
o1js-scan src --json
# SARIF 2.1.0 for GitHub code scanning (writes o1js-scan.sarif by default)
o1js-scan src --sarif
noir-scan . --lang noir --sarif noir.sarif
# choose which severity fails CI (critical|high|medium|low|none; default high)
o1js-scan src --fail-on medium
# progressive/power-user gate (equivalent to --fail-on medium)
o1js-scan src --strict
# test code is excluded by default (both backends); opt back in
o1js-scan src --include-tests
# example code is downgraded to LOW by default; keep original severity
o1js-scan src --include-examples
# a path with no o1js / Noir source exits 2; opt out where that is expected
o1js-scan packages/ --allow-empty
o1js-scan --version
--fail-on レベル(デフォルトは high)以上の検出結果が存在する場合、終了コードは 1、それ以外は 0 です。そのため、そのまま CI に組み込めます。デフォルトでは、low/medium の検出結果(以下に示す情報提供目的の recipient ルールを含む)はビルドを 失敗させません。レポートのみを行うには --fail-on none を、low 重大度の検出結果を助言的なものとして扱いつつより厳格にゲートするには --strict(--fail-on medium の省略形)を使用します。この 2 つのオプションは相互排他的であるため、CI 設定が曖昧になることはありません。スキャンパスが存在しない場合は終了コード 2 で stderr にエラーを出力するため、タイプミスがクリーンな実行として CI を黙って通過することはありません。存在するものの、アナライザーが読み取れる o1js または Noir のソースを含まないパス(たとえば o1js プロジェクトに対して --lang noir を指定した場合)も同様です。それが正当な場合は --allow-empty を渡してください。すべての実行は、1 行のサマリー(重大度別の件数とゲート判定)を stderr に出力します。
テストコードはデフォルトで除外されます — 両方のバックエンドで。 テストは、アサートがそれらを拒否することを証明するために、意図的に無効な値や不正なトランザクションを構築するため、そこでの検出結果は回路のバグではなくテストの目的そのものです。ファイルがテストコードとして扱われるのは、以下の場合です:
*.test.ts / *.spec.ts(および .js/.jsx/.tsx/.mjs/.cjs の各バリアント)、または *_test.nr / test_*.nr に一致する場合;test/、tests/、__tests__/、spec/、__mocks__/ ディレクトリの下にある場合;#[test] / #[test(...)] 属性を持つ場合、または mod test { … } / mod tests { … } ブロックの内部にある場合 — ブロックスコープであるため、本番ファイルの末尾にあるテストモジュールがそのファイルの残りの部分を黙らせることはありません。これらをレポートするには --include-tests を渡してください。
サンプルコードは格下げされ、破棄されません。 examples/ または example/ ディレクトリ内、あるいは *.eg.ts(.nr およびその他の JS/TS 拡張子も同様)という名前のファイル内の検出結果は、注記付きで LOW に格下げされます — 引き続きレポートされますが、ビルドを失敗させることはできなくなります。サンプルコードは意図的に簡略化されており、フレームワーク自身のサンプルを脆弱性としてフラグ立てするのはノイズです。しかし、サンプルコードはテストコードよりもはるかに頻繁に 本番環境にコピーされる ため、隠すのではなく格下げされるのです。元の重大度を維持するには --include-examples を渡してください。
いずれかのポリシーが適用されるたびに、その旨を示す行が stderr に出力されます — たとえば 6 file(s) skipped as test code, 1 finding(s) downgraded as examples — そのため、静かなスキャンが黙って静かになることはありません。件数は SARIF の invocation.properties にも表示されます。トレードオフに注意してください:検出は パスベースのみ(describe(/it( の解析は行いません)であるため、tests/ の下に置かれた本番回路は スキップされます — stderr の行がそれに気づく手段です。
ツリーを走査する際にスキップされるディレクトリ: node_modules、target(nargo)、.git、dist、build、__pycache__、.venv、venv。
ゲートを緩めることなく、トリアージ済みの検出結果を、フラグが立てられた行またはその上の行へのインラインコメントで黙らせることができます:
this.send({ to, amount }); // o1js-scan-disable-line O1JS_UNCONSTRAINED_WITNESS
// o1js-scan-disable-next-line
this.send({ to, amount });
let inv = unsafe { hint(x) }; // o1js-scan-disable-line NOIR_UNCONSTRAINED_WITNESS
1つ以上のルールIDを指定すると、それらのルールのみを抑制します。IDを指定しない裸のディレクティブは、対象行のすべてのルールを抑制します。
ライブラリとして:
from o1js_scan import analyze_file, analyze_project
for path, finding in analyze_project("src", lang="auto"):
print(path, finding.rule_id, finding.severity.value, finding.title)
数行でスキャナーをCIに追加できます。検出結果はPRの差分へのアノテーションとして、またリポジトリのSecurity → Code scanningタブにアラートとして表示されます。
# .github/workflows/o1js-scan.yml
name: o1js-scan
on: [push, pull_request]