MINARA
セキュリティ

監査とオーバーライド

ゲートの判断はすべてログに記録されます。信頼済みのワークフローはポリシーレベルで免除できます。無効化スイッチはインシデント対応のために存在し、常に履歴を残します。

先週月曜日、実行しようとしたスクリプトがブロックされました。「キャンセル」をクリックした記憶はあるのですが、どのスクリプトだったか?あるいは、エンドツーエンドでレビュー済みの自動化があるのに、毎回同じ確認が求められる。ゲートに通してもらうことはできないでしょうか?

このページでは、オペレーター側の 3 つのレバーについて説明します。過去の判断の確認、ワークフロー単位の免除設定、そして緊急時の無効化スイッチです。

設計思想:可視・介入可能・常に監査済み

ゲートを通るすべてのコードパスで、次の 3 つの性質が保たれます。

  1. 可視。 ゲートの判断(reject / confirm / allow)は、必ず 1 件の監査エントリとして記録されます。オペレーターはいつでも過去に遡って確認できます。
  2. 介入可能。 信頼済みのワークフローは script_risk_policy で特定のスクリプト本体を事前承認できます。ただし、免除自体も監査エントリを書き込みます。「ポリシーにより承認された」のか「スクリプトが本当にクリーンだった」のかを区別できます。
  3. 常に監査済み。 インシデント時に DISABLE_SCRIPT_RISK_GATE=1 を設定した場合も、バイパスされた呼び出しはすべて bypassed_by="env_global" と記録されます。どの利用パターンでもゲートが無音になることはありません。

この形をとる理由は? 信頼は厳格さではなく透明性から生まれます。厳格一辺倒のゲートは迂回されます。環境変数を設定してそのまま忘れる、という使い方です。明確なバイパス手順を提供しつつその利用を記録するゲートは、柔軟性(実際のワークフローを本番に出せる)と説明責任(インシデント後に何が起きたかを再構築できる)を両立します。

判断の確認:minara script-risk list / show

まず「今日起きたこと」から確認しましょう。

# 全ツールの直近 50 件の判断
minara script-risk list

# 直近 24 時間の reject のみ
minara script-risk list --verdict=reject --since=2026-05-14

# 特定セッションのすべての判断
minara script-risk list --session=abc-123

# 特定ワークフロー定義内のすべての判断
minara script-risk list --workflow=wf_daily_rebalance

# ツールで絞り込む
minara script-risk list --tool=execute_code

# 1 件の判断の詳細(全 finding、全証拠行)
minara script-risk show <decision-id>

# マシンリーダブル出力
minara script-risk list --verdict=confirm --json

フラグは組み合わせて使えます。目的はフォレンジックです。昨日ブロックされたものを把握したいときは、まず広く(--verdict=reject)絞り込み、そこから(--session--workflow--tool--since)と狭めていきます。

HTTP 相当のエンドポイントでも同じデータ形式で取得できます。

GET /v1/admin/script-risk/decisions?verdict=reject&tool=terminal&since=…&limit=…
GET /v1/admin/script-risk/decisions/<id>

ゲートウェイを自分以外に公開する場合は、このエンドポイントをリバースプロキシでラップしてください。

監査エントリに含まれるもの・含まれないもの

すべての判断について記録される項目:

  • 判断のタイムスタンプ、ツール名、ツール呼び出し ID(判明している場合)、セッション ID
  • 判定(reject / confirm / allow)と findings 配列。各 finding はカテゴリ、1 行のメッセージ、マスク済みの証拠テキスト、本体内の行番号を持ちます
  • body_sha256:解析した本体のハッシュ
  • 確認ウィジェットを経由したパスの場合は user_decision(execute / cancel / timeout)
  • 明示的なポリシーで通過した場合は bypassed_by(env_global / workflow_policy / null)

明示的に記録しない項目:

  • スクリプト本体そのもの
  • マスクルールに一致した生の秘密鍵、Authorization ヘッダー、Cookie 値、Bearer token
  • スクリプトの stdout / stderr

本体を記録しない理由は? 秘密鍵を含むスクリプトを記録すると、永続化した瞬間に監査ログが第 2 の漏洩チャネルになるためです。sha256 を保存することで「同じスクリプトを以前に見たか?」(yes/no)を答えられ、マスク済みの証拠でどのルールが発動したかわかります。シークレットを長期保存テーブルにコピーせずに済みます。

トレードオフとして、事後にスクリプト本体をバイト単位でリプレイすることはできません。その代わり、バックアップ・共有・grep が安全な監査ログが得られます。

ワークフロー免除:script_risk_policy

次のようなケースがこの機能の動機です。毎日 minara swap USDC ETH 100 を呼び出す積立投資 (DCA) ワークフローがあるとします。スクリプトは自分で書き、監査済みで、本体は変わりません。毎回確認を求められるのは単なる摩擦です。

ワークフロー定義の中で次のように指定します。

script_risk_policy:
  approved_body_sha256:
    - "abc123…"      # 監査済みスクリプトの sha256
  allowed_categories:
    - fund_moving_cli  # このワークフローは明示的に
                       # minara swap の呼び出しを許可される

ワークフローがアクティベートされると(workflow_activate 経由。これ自体が資金移動の二段階確認を要します)、ポリシーが登録されます。以降、このワークフローが一致するスクリプト本体を実行した場合、YELLOW の finding はサイレントにダウングレードされます。ただし監査エントリには bypassed_by="workflow_policy" が記録されるため、ゲートがブロックしなかった事実を事後に確認できます。

次のような用途に使用してください:

  • 本体が安定している事前監査済みスクリプト(ハッシュチェックで特定リビジョンに固定されます)
  • 特定カテゴリのアクションがまさに目的であるビジネスワークフロー(スワップを行う DCA ワークフロー、approve を呼ぶリバランスワークフローなど)

次の用途には使用しないでください:

  • 汎用的なキャッチオール的ワークフロー。「この免除は何のためか?」を 1 文で答えられるほど、ポリシーは絞られている必要があります。
  • すべてのカテゴリをホワイトリスト化する。それはワークフロー単位でゲートをオフにするのと同じです。
  • 実行ごとに動的に生成されるスクリプト。本体が変わると sha256 が一致せず、ポリシーはサイレントにフェイルクローズします(動作としては問題ありませんが、恩恵のない複雑さが増えるだけです)。

ポリシーバイパスが YELLOW には効いて RED には効かない理由は? YELLOW は「正当だが高リスク」を意味します。minara swap は誰かが自動化したいと思う実際の操作です。「この特定ワークフローはそれを行ってよい」と言う場所がポリシーです。

RED は「正当なビジネス上の理由がない」を意味します。大量削除、IMDS 情報窃取、インラインの秘密鍵などは、正当なワークフローシナリオがありません。ワークフローが RED に達したとき、答えはポリシーを広げることではなく、ワークフローのスクリプトを修正することです。RED は意図的にポリシーバイパス不可です。

グローバル無効化スイッチ:DISABLE_SCRIPT_RISK_GATE

環境変数 DISABLE_SCRIPT_RISK_GATE=1 を設定すると、ゲートを完全にスキップします。RED・YELLOW ともに通過します。バイパスされた呼び出しはすべて bypassed_by="env_global" とマークされた監査エントリとして記録されます。

年に数回程度の使用を想定しています:

  • 本番インシデント対応。 真のフォールスポジティブが実際のビジネスをブロックしており、修正を出すまでの 10 分が必要な場合。環境変数を設定し、復旧作業を行い、解除してから、該当ルールを改善する Issue を起票します。
  • 完全隔離された CI。 バックテスト、エンドツーエンドテスト、リグレッションスイートで、すべての呼び出し元が非対話型であることが証明されており、テスト環境がサンドボックス化されている場合。

次の用途には使用しないでください:

  • 「確認ウィジェットが面倒」という理由。それはセーフティが機能している証拠です。
  • 「チームで Agent を動かしているのでポリシーを監査したくない」という理由。それはチーム全体を、Layer 1 をすり抜けた攻撃者にさらすことになります。
  • シェルのプロファイルに設定して忘れること。次の無関係なセッションに引き継がれます。

監査の観点から、誤用を発見できます。

minara script-risk list --json \
  | jq '.decisions[] | select(.bypassed_by == "env_global")'

記憶にない期間に env_global バイパスが見つかった場合、意図しない場所で環境変数が設定されています。

環境変数のフルリファレンスは環境変数 → DISABLE_SCRIPT_RISK_GATE を参照してください。

オペレーターの責任

Layer 4(ゲート)、Layer 3(資金移動確認)、Layer 2(OS jail)は多くをカバーしますが、避けられないオペレーター側の衛生管理を代替するものではありません。

  • Minara インスタンスごとにオペレーターは 1 人。 Minara はシングルユーザーの取引用に設計されており、マルチテナント SaaS ではありません。チームで共有する Web UI はサポートされていない構成です。各オペレーターは自身のインスタンスを実行してください。
  • 秘密鍵は .env に置き、リポジトリには含めない。 また .envchmod 600 にしてください。ゲートは実行時に Layer 4 が未認可ファイルを読み取るのを防ぎますが、シェルで cat .env するのは防げません。
  • RED が出たらバイパスしようとしない。 自分が実行したスクリプトが高信頼度の悪意あるパターンに一致した理由を立ち止まって考えてください。95% 以上のケースはフォールスポジティブではなく、ゲートが正常に機能している証拠です。
  • YELLOW が出たら証拠を読む。 一致した行を 2 秒で読むことが、ウィジェット全体の目的です。「実行」を反射的にクリックする癖をつけると、自分が最も弱いリンクになります。
  • MINARA_SKIP_FUND_CONFIRM=1DISABLE_SCRIPT_RISK_GATE=1.zshrc.envrc、systemd ユニットに書かない。 特定のスクリプトで本当に必要な場合は、そのコマンドに対してインラインで設定し、直後に解除してください。
  • 監査ログのバックアップは重要です。 script_risk_decisions SQLite テーブルはフォレンジックの記録の一部です。ログと同等の扱いをしてください。

目次