mcpField
M
mantis-reproduce

mantis-reproduce

github.com/googleセキュリティ2.7k1.5k 回インストール3 日前に更新
リポジトリ

mantis-reproduce について

mantis-reproduceは、報告された脆弱性を検証するセキュリティエンジニア向けのスキルです。隔離環境でPoCやペイロードを作成・実行し、結果を記録するとともに、スナップショットの変化やパッチ後の再攻撃検証に対応します。

主な機能

  1. 01PoCスクリプトまたはクラッシュペイロードの作成
  2. 02隔離環境での再現テスト実行
  3. 03スナップショットに基づく発見事項の確認
  4. 04パッチ後の再攻撃検証
  5. 05結果と再現用成果物の記録

活用シーン

  1. 01報告された脆弱性が発火するか検証
  2. 02パッチ適用済みコピーで発見事項を再確認

インストール

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

$npx skills add https://github.com/google/mantis --skill mantis-reproduce

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

詳細

翻訳後に原文が更新されています。最新の内容は原文をご確認ください。

再現(/mantis-reproduce)

システムの目標

統合テストエンジニアです。クラッシュ再現器または入力を設計し、 隔離されたサンドボックス環境内で実行して、バグを実証的に検証します。

コマンドの定義

  • コマンド: /mantis-reproduce [--reattack] [--finding_id=<uuid>] [--force] [--target_root=<path>] [--state_root=<path>] [--snapshot_root=<path>] [--snapshot_id=<SNAPSHOT_ID>] [--snapshot_pinned=<true|false>]
  • 説明: セキュリティ上の欠陥を検証するため、クラッシュ再現器を生成して実行します。
  • パラメーター:
    • --reattack:パッチ検証の一環として実行する際に、再攻撃の結果を切り分けます。
    • --finding_id:再現する特定の検出 UUID です。必ず指定する必要があり、--reattack を指定する場合は必須です。
    • --force:対象となる通常実行の適格性チェックを上書き/回避します。
    • --target_root:テスト対象のコードベースのルートへのパスです (デフォルトは .)。指定した場合は最優先され、--snapshot_root を上書きします(ブロック A のステップ 1a)。このツリーではセンチネルチェックがスキップされます(例:再攻撃検証中のパッチ適用済みシャドウ)。
    • --state_root:workspace/ を含む Mantis 状態ディレクトリのルートへのパスです (デフォルトは .)。
    • --snapshot_root:この実行で固定された不変のコードスナップショットのルートです。 --target_root が指定されていない場合に、ブロック A(ステップ 0)で使用されます。
    • --snapshot_id:固定されたスナップショットの SNAPSHOT_ID 文字列です。ステップ 0 のブロック A(センチネル)とブロック B(スナップショット一致チェック)で使用されます。
    • --snapshot_pinned:false の場合(パッチ適用済みシャドウへの再攻撃中に mantis-patch によって設定)、この実行では再現サブエージェントはスナップショットの センチネル/一致チェックを必ずスキップします。--target_root のツリーが最優先され、センチネルチェックの対象外となります(ブロック A のステップ 1a)。

入出力の契約

  • 読み取り対象:
    • state_root/workspace/findings/(実現可能/条件付きの調査結果)。
    • target_root/(トリガー経路を分析するリポジトリのソースファイル)。
    • state_root/workspace/archive/.repro_attempts.json。
    • state_root/workspace/.mantis_state.json(現在のループのパスを追跡するため)。
  • 書き込み対象:
    • PoC の再現ファイル(例:state_root/workspace/reproducers/ 内の poc_[uuid].py または crash_[uuid].payload)。
    • 通常実行の場合:state_root/workspace/findings/ 内の調査結果をその場で更新します("repro_status"、"repro_file_path"、"run_command"、"repro_output" を設定し、履歴を追加します)。暫定的に有効な場合は、ステータスを "VALID" に更新します。
    • --reattack を付けて実行する場合:state_root/workspace/findings/ 内の調査結果をその場で更新します("reattack_status"、"reattack_file_path"、"reattack_run_command"、"reattack_output"、"reattack_variants" を設定し、ステージ "reattack" の履歴を追加します)。"repro_*" フィールドや "status" は変更しません。例外: ステップ 6 の INV-1 に従い、patch_status をアトミックに格下げする場合があります(VERIFIED_SECURE と failed_to_bypass 以外の reattack_status を併存させてはなりません)。
    • state_root/workspace/archive/.repro_attempts.json をアトミックに更新します。
    • 更新した調査結果に "repro_snapshot_id"/"reattack_snapshot_id" を記録し、.repro_attempts.json の値を {count,last_snapshot} オブジェクトとして保存します(整数のみの形式も引き続き正しく読み取れます)。
  • 前提条件:
    • state_root/workspace/findings/ に調査結果が存在すること。
    • サンドボックス/コンテナーのランタイム環境が利用可能であること。
  • 冪等性の保証:
    • 調査結果をその場で更新します。state_root/workspace/archive/.repro_attempts.lock のファイルロックと、アトミックな一時ファイルの置き換え(state_root/workspace/archive/.repro_attempts.json.tmp に対する os.replace)を使用して、並行実行の安全性と再試行の安定性を保証します。
    • スナップショットを考慮します。調査結果のスナップショットが一致しなくなった場合は PoC を再生成し、到達したシンクの証拠がない場合は否定的な判定を出しません。スナップショット不一致時の再攻撃判定には、C5 の未パッチベースライン再実行(ステップ 6)が適用され、従来の一律な拒否に優先します。failed_to_bypass 判定は、C5 により現在のスナップショットでも未パッチベースラインが引き続きトリガーされることを確認した後にのみ記録されます。

手順

ステップ 0:ロケーターの解決とスナップショットの一致(最初に実行)
LOCATOR RESOLUTION (before reading ANY target code or artifact):
0. ROLE: If this skill NEVER reads target source (report, calibrate, reflect),
   you are a FINDINGS-ONLY stage: skip steps 2-6; still read active_snapshot from
   state for provenance/annotation; NEVER stop merely because a code root is unset.
1. Determine CODE_ROOT, in this priority order:
   a. If --target_root is passed on THIS invocation, CODE_ROOT = --target_root.
      It is AUTHORITATIVE and OVERRIDES SNAPSHOT_ROOT and the state fallback
      (used when a caller hands you a prepared tree, e.g. a patched shadow).
   b. Else if --snapshot_root (or SNAPSHOT_ROOT) is passed, use it.
   c. Else read state_root/workspace/.mantis_state.json (state_root from
      --state_root if passed, else ./workspace/... relative to the current dir)
      -> active_snapshot.root / .snapshot_id / .snapshot_pinned.
   d. Else (no arg AND no readable active_snapshot): CODE_ROOT = current directory,
      treat snapshot_pinned = false (MODE-OFF). Do NOT stop.
2. SENTINEL CHECK (only if snapshot_pinned is true AND you did NOT take path 1a):
   verify CODE_ROOT/.mantis_snapshot_id exists and equals SNAPSHOT_ID. If missing
   or different -> STOP "snapshot sentinel mismatch". (A --target_root tree (1a) is
   deliberately mutated and is sentinel-EXEMPT.)
3. PATH FIELDS:
   - SNAPSHOT-RELATIVE (read under CODE_ROOT): code_paths entries; plan target_files
     that are file paths. Strip ONLY a trailing ":<digits>". A code_paths entry
     containing "://" is a URL/endpoint, NOT a file read. A code_paths entry that is
     NOT of the form <existing-path>:<integer> is a non-source LOCATOR
     (symbol/offset/endpoint): only check that the artifact/symbol exists; skip ALL
     line-range and line-existence logic.
   - STATE-RELATIVE (read/write under state_root/workspace, NEVER prefix CODE_ROOT):
     kb_references, repro_file_path, reattack_file_path, helper scripts, report
     files, and all state/findings JSON.
4. Never WRITE under CODE_ROOT when snapshot_pinned is true. Any command that
   compiles, generates, or writes artifacts MUST run in a PRIVATE SHADOW copy
   (mktemp -d from CODE_ROOT), never with cwd=CODE_ROOT. Read-only inspection may
   cd into CODE_ROOT.
5. VCS-METADATA CARVE-OUT: history-log extraction and any VCS diff/blame command
   run in the LIVE repository root (which still has .git/.hg/.repo), NOT CODE_ROOT
   (the snapshot copy strips VCS metadata). Do NOT stop merely because CODE_ROOT
   lacks .git/.hg/.repo.
6. Every shell command uses ABSOLUTE paths and sets its own working directory on
   that call. Do NOT assume the working directory persists between calls.

メモ

現在のパスの確認(防御的措置。保証の主体は mantis-pipeline-adapter シナリオ 2 に従うハーネスです): active_snapshot が存在し、かつ active_snapshot.pass != state.pass_number の場合、このパスではスナップショットが STALE であると見なします。処理を停止して "stale active_snapshot: pass mismatch" とするか、HALT に相当する状態に移行します(実質的に snapshot_pinned は false となり、権威ある判定は行わず、Block B は NOT_MATCHED、再現は not_attempted となります)。これは、Stage 15 のパス増分後にカスタムハーネスが active_snapshot を保持し、再ピン留めしなかった場合を検出します。リファレンスのメタエージェントは各パスで再ピン留めするため、この確認がそこで発動することはありません。Block B 自体ではこれを検出できません(pass は考慮せず、snapshot_id のみを確認します)。

SNAPSHOT MATCH CHECK for finding F (decides MATCHED vs NOT_MATCHED):
1. If snapshot_pinned is false -> NOT_MATCHED. Stop.
2. Read F.discovery_commit:
   - missing OR empty OR the literal "MIXED" -> NOT_MATCHED.
   - not exactly equal to SNAPSHOT_ID          -> NOT_MATCHED.
   - exactly equal to SNAPSHOT_ID              -> MATCHED.
There is no other route to MATCHED; never fuzzy-compare. The global "default the
field and proceed" backward-compat rule does NOT apply to discovery_commit:
absent = NOT_MATCHED. (There is NO separate "dirty" gate: a dirty tree's
SNAPSHOT_ID already embeds the working-tree content hash, so within-pass findings
MATCH and cross-pass bare-commit findings do not.)

注:パッチャーから --target_root=<shadow>(パッチ適用済みコピー)を指定して呼び出された場合、Block A のステップ 1a ではそのシャドウを権威ある CODE_ROOT とし、センチネルの確認をスキップします(シャドウは意図的に変更されています)。それ以外の場合、CODE_ROOT はピン留めされたスナップショットであり、センチネルが一致している必要があります。更新するすべての調査結果に、現在の SNAPSHOT_ID(--snapshot_id または state の active_snapshot.snapshot_id から取得)を repro_snapshot_id(通常実行)または reattack_snapshot_id(--reattack)として記録します。

確認済みのセキュリティ上の欠陥を再現するための概念実証再現スクリプト(Repro)または生の入力ペイロードファイルを作成します。

以下の制約に従って、再現ステージを実行します。

  1. 実現可能な調査結果を読み込む:

    • --finding_id が指定されている場合:
      • その finding のファイル (state_root/workspace/findings/<uuid>.json) のみを読み込みます。存在しない場合は終了します。
      • --reattack が指定されている場合: 読み込んだ finding に対して期待されるパッチワークフロー 状態を強制します:
        • finding の "status" は "VALID" または "PROVISIONALLY_VALID" でなければなりません。
        • finding の "repro_status" は "reproduced" でなければなりません。
        • finding の "patch_status" は "MITIGATION_PROPOSED" であってはなりません。 (VERIFIED_SECURE は許可されます: 再攻撃の結果が failed_to_bypass でない場合、 後述の C5 がこれをアトミックにダウングレードします。)
        • これらの条件が満たされない場合は、無効な状態を説明するエラーを出力して終了します。
      • --reattack が指定されていない場合 (ターゲット指定の通常実行):
        • --force が指定されていない場合、標準の適格性フィルターを強制します:
          • finding の "status" は "VALID" または "PROVISIONALLY_VALID" でなければなりません。
          • finding の "production_viability" は "VIABLE"、 "SAMPLE_OR_TEST"、または "CONDITIONAL_VIABLE" でなければなりません。
          • これらの条件が満たされない場合は、無効な状態を説明するエラーを出力して終了します。
        • --force が指定されている場合、これらの適格性チェックをバイパスします。
    • --finding_id が指定されていない場合:
      • 制約: --reattack が指定されている場合は終了します (--finding_id が必要です)。
      • state_root/workspace/findings/ ディレクトリ内の JSON ファイルを読み込みます。
      • 厳格な適格性フィルター (通常実行): 以下の条件を満たす finding のみを含めます:
        • "status" が "VALID" または "PROVISIONALLY_VALID" である。
        • "production_viability" が "VIABLE"、"SAMPLE_OR_TEST"、または "CONDITIONAL_VIABLE" である (viability をチェックしない場合はこの viability フィルターを スキップしてもよいが、status は常にチェックすること)。
      • 該当する finding が存在しない場合は、ユーザーに通知して終了します。

    Tier 0 — 構造的到達可能性の事前チェック (勧告的なキューの並べ替え): 構造的コードインデックス (mantis-structural-index) が利用可能な場合、コードを作成する前に query_structural_index.py (find_callers) を照会して、公開エントリポイントから 脆弱なシンクまでの AST 呼び出しパスが存在するかどうかを確認してもよい (MAY)。この照会を使用して 候補の実行順序を優先順位付けします (検証済みの AST 到達可能性を持つ finding を最初に処理します)。

    • 重要なヒント限定のガードレール: AST 到達可能性は順位付けのヒントにすぎません。 コールグラフはマクロ、関数ポインター、動的ディスパッチ、およびインターフェーステーブルを見逃します。 呼び出しパスが存在しないことは、finding を却下したり、再現をスキップしたり、 failed_to_reproduce を設定したりする理由になってはなりません (MUST NEVER)。

    スナップショットドリフトチェック: 読み込んだ各 finding について、既に repro_snapshot_id を持ち、かつ Block B (ステップ 0) が NOT_MATCHED を返す場合、保存されている PoC/オフセットはすべて STALE として扱います: 現在の CODE_ROOT に対して再現プログラムを ゼロから再生成します (古い行番号/アドレスを再利用しないでください)。Block B が MATCHED の場合は、既存の PoC を再利用してもかまいません。

  2. 厳格なホスト分離の制約:

    • ホストでのコマンド実行は厳格に禁止されています。ターミナル/シェル実行ツールを使用して 親ホストのターミナル上で直接コマンドを実行しないでください。
    • すべての再現プログラムの実行は分離された環境で実行しなければなりません。環境が提供する コンテナ化またはサンドボックス実行ツールを使用してください。メモリ安全性の PoC では、 ネットワークアクセスとファイルシステムへの書き込みを可能な限り制限してください。 ロジック/認証の機能テストでは、必要に応じてローカルネットワークサービスを有効にしても かまいませんが、環境を外部インターネットに決して公開しないでください。
  3. 再現手順の作成と実行: 自己完結型のテストスクリプト (例:poc_[uuid].py または同じディレクトリ内の C 再現ファイル)を作成するか、 対象のバグを引き起こす生のクラッシュ入力データペイロード (例:crash_[uuid].payload)を作成してください。生成するすべての PoC/再攻撃スクリプトとペイロードは、必ず state_root/workspace/reproducers/ ディレクトリ内に作成してください(target_root ディレクトリ内には絶対に作成しないでください)。 ファイルを書き込む前に、 親ディレクトリ state_root/workspace/reproducers/ が存在することを確認してください (例:mkdir -p を使用)。コードパスと制約を慎重に分析してください。最初の再現の試みが 失敗した場合は、観察結果に基づいて、検出内容の詳細(入力パス、パラメーター、 前提条件など)が少し誤っていないかを評価し、修正を試みるために検出内容を動的に調整してください。 複数の方法と調整を試してもトリガー可能なパスを見つけられない場合は、試みを中止してください。 直接 failed_to_reproduce と判定してはいけません。中止の判断は、Step-5 の Block F(シンク到達の証拠)ゲートを通してください。ハーネスが脆弱なエントリーポイントに到達したことを 証拠で確認できるものの、バグが発生しなかった場合は failed_to_reproduce と分類します。 証拠がない場合(セットアップ/ビルドの失敗、終了コード 127、「No such file」、またはシンクに到達していない場合)は not_attempted(再試行可能)と分類します。セットアップ/ビルド失敗による生の否定結果は、 再試行回数の上限を消費し、本物のバグをひそかに取りこぼします。

    スクリプトまたはペイロードを実行するには、環境で利用可能な実行ツールまたはコンテナ化ツールを使用して、 コードを安全に実行してください。対象に最適なランタイムイメージとフラグを選択してください。 すべてのコンパイルコマンドとテスト実行コマンドは、必ずプライベートなビルドシャドウ内で実行し、 Cwd=CODE_ROOT では絶対に実行しないでください(スナップショットは読み取り専用です — Block A の手順 4)。 コンパイル前に BUILD_ROOT=$(mktemp -d) を作成し、CODE_ROOT の内容をそこにコピーしてください (例:cp -a CODE_ROOT/. BUILD_ROOT/)。すべてのコンパイル/テストコマンドを Cwd=BUILD_ROOT で実行し、 終了時に BUILD_ROOT を削除してください。生成した PoC ファイル自体は state_root/workspace/reproducers/(STATE 相対パス)に置き、その絶対パスを "run_command"/"reattack_run_command" に格納してください。

    サニタイザーを使ったコンパイル(C/C++ の対象): バグの種類がメモリー安全性または未定義動作の場合は、 最低限 -fsanitize=address,undefined(ASan + UBSan)を指定してコンパイルしてください。データ競合の場合は -fsanitize=thread(TSan)を追加します。TSan は ASan と同時に使用できないため、別ビルドを使用してください。 使用可能なスタックトレースを得るには -fno-omit-frame-pointer を使用してください。 これらのフラグにより、何も表示されずに終了コード 0 となるバグが表面化します(UBSan はデフォルトで recover-mode で動作します)。MSan に関する注意: MemorySanitizer (-fsanitize=memory)は未初期化メモリーの読み取りを検出しますが、libc や C++ ランタイムを含む 依存関係チェーン全体を MSan で計測する必要があります。任意の対象では通常それが満たされないため、 安易に -fsanitize=memory を使ってビルドすると、誤った WARNING: MemorySanitizer トレースが出力されます。また、MSan は 1 つのビルドで ASan と同時に使用できません (フラグを追加するのではなく、別ビルドを使用してください)。完全に計測された MSan 環境を保証できない場合は、 -fsanitize=memory を追加しないでください。誤っている可能性のある MSan トレースを信頼するのではなく、 ASan + UBSan を使用してください。正規の MSan 計測ビルドを利用できる場合に、MSan の出力を証拠として認識することは 引き続き正しい対応です(Step 5)。サニタイザーの一貫性に関するガードレール: ベースライン、 攻撃、再攻撃の各実行では、必ず同じサニタイザーフラグを使用してください。異なるサニタイザーを使うと、 バグが隠れて誤検知の否定結果(INV-2)や誤った VERIFIED_SECURE(INV-1)につながる可能性があります。 Block G を参照してください。

    {TARGET_ROOT} トークンの置換(番号付きの手順):

    1. run_command または reattack_run_command を記述する際、対象ツリーを参照するパスにはリテラルトークン {TARGET_ROOT} を使用してください。
    2. 実行時に、再現環境が {TARGET_ROOT} を実際のルートに置き換えます。
      • 初回実行時:CODE_ROOT(スナップショットまたは --target_root)。
      • 再実行時(再攻撃または再試行):現在の CODE_ROOT / --target_root / active_snapshot.root。
    3. これにより、スナップショットの GC 後も保存されたコマンドが正しく解決されます。 保存されたコマンドに絶対パス .mantis_snapshots/pass_<N> を 決して埋め込まないでください。

    適切な環境で再現を実行してください: 対象がファームウェアの場合、スクリプトを作成して qemu、unicorn、または Firmadyne 経由で起動できます。バイナリの場合は、 動的計装または標準的な実行を利用できます。アーティファクトに対して 動作するハーネスを構築するため、最善の判断をしてください。

    • 並列軌跡探索と段階的な反復再現:

      • 並列軌跡探索(幅優先): サブエージェントを利用できる場合は、 多様な論理的アプローチでバグの再現を試みるワーカーを同時に稼働させます。 いずれかの軌跡で成功したら、そのペイロードをただちに採用し、他の試行は破棄して、 「諦める」ループを回避し、計算コストを削減します。

      • 段階的な反復再現(深さ優先でのペイロード改良): 各軌跡ワーカー(または 単一のエージェント)は、最初からエンドツーエンドで一度に起動するのではなく、 段階的なエスカレーション手順(Tier 1 → Tier 2 → Tier 3)を用いて、 トリガーペイロードを少しずつ改良します。

    • 段階的な反復実行手順:

      • Tier 1(マイクロハーネス/シンクロジックの検証): 脆弱な関数やモジュールを 直接呼び出す軽量なテストを構築し、バグに関する仮説が単独で妥当かどうかを検証します。
      • Tier 2(サブシステム/インターフェースの検証): 入力のシリアライズ、パーサー、 ルーティング、認証ラッパーを通してペイロードを渡し、サニタイズや切り詰めが行われずに 入力が中間処理を通過することを検証します。
      • Tier 3(サンドボックス化されたサービス全体/E2E の検証): 独立して実行できる PoC を、隔離されたサンドボックス(Docker、QEMU、VM)内の公開 API 経由で 対象サービスに対して実行します。Block F に従い、これが権威ある reproduced 判定になります。
    • 重要なステップ 4 の Tier 1 ハードゲート(失敗時は閉じる):

      • Tier 1 と Tier 2 は内部的な足掛かりにすぎません。Tier 1 または Tier 2 の 実行結果に基づいて、repro_status = "reproduced" または "statically_confirmed" を決して記録してはいけません。
      • クラッシュが、プライベート/static 関数を呼び出す、または公開 API を バイパスする直接呼び出しハーネスをコンパイルした場合にのみ発生し(ステップ 4)、 Tier 3(公開 API/サンドボックス化されたサービス)経由で発生させるよう ペイロードをエスカレーションできない場合は、必ず終了し、 "Internal Invariant Protection" を根拠の詳細に記載して repro_status = "failed_to_reproduce" を記録してください。
    • 試行回数の上限管理とローカル再試行:

      • Tier 3 未満の足掛かり試行のサブ予算: 内部的な Tier 1 および Tier 2 の 試行実行は、会話ごとに最大 3 回までのローカル実行ステップに制限され、 state_root/workspace/archive/.repro_attempts.json の検出結果ごとの 絶対試行回数には加算されません。
      • 絶対試行回数の上限カウント: Tier 3 のサンドボックス化されたサービス全体の 実行(またはエンドツーエンドの再現実行)のみが、ハード上限 6 回(セクション 6)に 向けて絶対試行回数に加算されます。
      • 会話内の再試行: ある Tier が失敗した場合は、ログを確認し、ペイロードの パラメーターを調整し、ハーネスの設定を修正してから、オーケストレーターに報告する前に アクティブな会話内で最大 2~3 回再試行します。
ステップ 3a:バリアントの探索(再攻撃のみ、必須)

--reattack が指定された場合、元の PoC を再実行するだけでなく、N ≥ 3 個の境界値を変更した入力バリアントを作成し、実行しなければなりません。スキーマ(schema.json)では、これを文字どおり「バリアント探索による再攻撃」と呼んでいます。元の PoC の再実行だけでは不十分です。PoC のバイト列そのものだけを対象とする過度に限定的なパッチは、自動修復で最も多く見られる失敗パターンです。バリアント探索は、インフラコストゼロでこれを防ぐ手段です。

レガシーの検出結果: reattack_variants がない(このルールが導入される前に作成された)検出結果は、初回の --reattack 実行時に、3 個以上のバリアント要件に基づいて再検証されます。これは意図的な動作です。従来の VERIFIED_SECURE 判定は、より厳格なゲートに引き上げられます。ただし、初回のバリアント探索実行時に、アーカイブ済みの検出結果全体で一度限りの変更が発生することを想定してください。

生成する内容(バグクラスに応じて):

  • メモリ安全性のバグ(バッファオーバーフロー、範囲外読み取り/書き込み、UAF、整数オーバーフロー): 次のうち少なくとも 3 個を作成します。

    • オフバイワン: len = bound、len = bound + 1、len = bound - 1。
    • サイズの変更: len ± 1、len = 0、len = SIZE_MAX、符号付きの長さの符号反転。
    • 同じシンクに至る別の経路: 脆弱なシンクに複数の呼び出し元から到達できる場合は、別の経路で到達するバリアントを作成します(例:異なる API エンドポイント、ファイル形式のバリアント、プロトコルコマンド)。
    • 型の混同/幅の不一致: 別の型経路を利用して、同じシンクに到達させます。
  • メモリ安全性以外のバグ(ロジック、認証、インジェクション、SSRF、パストラバーサル): 同じクラスの実際のバリアントを探索します。

    • 別のエンドポイント/パラメーター: PoC が /api/v1/echo を対象としている場合は /api/v2/echo を試す、または別のパラメーター名を試します。
    • 等価なペイロード: ..%2fetc%2fpasswd、..\\..\\、URL エンコード、二重エンコード、Unicode 正規化のバリアント。
    • 認証境界のバリアント: 異なるロール、空または null のトークン、別の権限昇格経路。
    • インジェクションのバリアント: '; EXEC--、 UNION SELECT、ブラインドバリアント、別のインジェクションポイント。
  • 並列バリアント生成(サブエージェントが利用可能な場合): バリアントの作成とテストを並列で行うため、サブエージェントを起動することを推奨します。各サブエージェントには 1 つの変更戦略を割り当て、バリアント PoC を作成させ、それがトリガーされたかどうかを報告させます。reattack_status を設定する前に、すべての結果を集約します。サブエージェントを利用できない場合は、順番に実行してください。

実行: 各バリアントを state_root/workspace/reproducers/ 内の個別のスクリプトとして書き込みます(例:reattack_variant_[uuid]_[N].py)。Step 3 と同じ分離制約の下で、それぞれをパッチ適用済みのシャドウ(--target_root)に対して実行します。Step 6 で、スキーマキー {"description": "...", "triggered": true/false} を正確に使用して、各バリアントを reattack_variants 配列に記録します。

判定ルール: reattack_status = "failed_to_bypass" とするには、パッチ適用済みのシャドウ上でバグのトリガーに失敗した、有効な入力バリアントが 3 個以上含まれる空ではない reattack_variants 配列が必要です。空または少数のセットでは、「すべてのバリアントが失敗した」が空虚に真となります。これは禁止です。真摯に検討したうえで、有意義なバリアントを 3 個未満しか作成できない場合は、VERIFICATION_INCOMPLETE(履歴メモ insufficient_variants)を上限とし、failed_to_bypass には絶対にしないでください。[] のケースを指定できるのは、C5 のベースライン失敗によりバリアント探索が中断された場合など、failed_to_bypass 以外のステータスの場合に限られます。

バリアントの有効性に関するガードレール: バリアントをバイパス(triggered = true により bypassed_patch となるケース)として数えるのは、次の両方を満たす場合に限られます。

  1. 同じ脆弱性クラス: バリアントは元のバグクラスを再現する必要があります — 同じシンク関数/サニタイザーのシグネチャ/クラッシュの種類です。無関係な OOM を引き起こす len=SIZE_MAX、別のエンドポイントが 404 を返した後に エラーになるケース、またはまったく異なるクラッシュなどの無意味なミュータントは、 バイパスとは見なしません。破棄し(無効だったことを示す説明を添えて triggered = false に設定し)、続行してください。メモリ以外のバグ (ロジック、認証、インジェクション)では、パッチ適用前の行番号を基準とする ±行の範囲ではなく、同じシンク関数または動作(例:同じ不正な操作が成功する、 同じインジェクションが実行される、同じシンク関数に到達する)を比較してください (パッチによって行番号が変わるうえ、メモリ以外のバグには正確なシンク行が ないこともよくあります)。トリガーされたバリアントが別のバグだと確実に確認 できない場合は、バイパスとして数えてください(フェイルクローズ: 誤った VERIFIED_SECURE より、誤った VERIFICATION_FAILED を優先します)。
  2. ステップ 4 に沿った有効な入力: バリアントは公開 API または内部不変条件を 適切に利用するものでなければなりません。ライブラリの実行不変条件を回避する 人為的なハーネス上の仕掛け(例:独自に割り当てたバッファーを使った非公開関数の 直接呼び出し)に依存してはなりません。

理想的には、元の PoC が実行されたものと同じベースラインである未パッチの シャドウでも、トリガーされた各バリアントが引き続きトリガーされることを確認し、 元のバグを再現しているのであって、単なるアーティファクトではないことを証明します。 スナップショットの不一致などで未パッチのシャドウを利用できない場合は、同じ サニタイザーのフレーム、同じクラッシュアドレス、同じロジック上の失敗など、 同一シンクであることを示す証拠を裏付けとして求めてください。

有効なバリアントが 1 つでもバグをトリガーした場合は、 reattack_status = "bypassed_patch" に設定し、INV-1 を適用してください (VERIFIED_SECURE → VERIFICATION_FAILED に格下げ)。

  • 再現ステータスの分類:

    • reproduced:PoC によって脆弱性のトリガーに成功しました。
    • failed_to_reproduce:PoC を実行しましたが、脆弱性をトリガーしませんでした。
    • statically_confirmed:環境上の制約(例:ハードウェアエミュレーターの 不足、外部サービスを利用できないこと)により再現できませんでしたが、 欠陥が静的解析で明白です(例:ハードコードされた認証情報)。これは強く 非推奨であり、最終手段としてのみ使用してください。
    • not_attempted:再現ステージを完全にスキップしました(例:インフラの セットアップ失敗、タイムアウト、または明示的なスキップ設定が原因)。
  1. 公開 API と内部不変条件に関する厳格な制約:

    • クラッシュ再現コードは、可能な限り公開 API を通じてコードベースとやり取りするか、 人為的で実現不可能なクラッシュを発生させないよう、ライブラリ全体の実行不変条件 (アロケーターのパディングなど)を厳密に守る必要があります。
    • ライブラリが保証するアロケーターのラッパー(例:png_malloc(rowbytes + 48)) を回避するカスタム割り当てバッファー(例:malloc(15))を使って、非公開/ static 関数を直接呼び出すハーネスをコンパイルする場合にしかクラッシュを 起こせないのであれば、その検出結果を「再現済み」としてはなりません。
    • 公開 API または標準のアロケーター・パディングを使ってクラッシュを トリガーできない場合は、「内部不変条件による保護」を理由に、検出結果を "failed_to_reproduce" と分類してください。
  2. 機能面とクラッシュを考慮した検証: バグの種類に応じて再現成功を分類するため、 stdout、stderr、終了コードなどの出力を分析してください。

    いずれかの否定的な結果(failed_to_reproduce、または --reattack モードでの failed_to_bypass)に分類する前に、このゲートを適用してください。

    REACHED-SINK EVIDENCE GATE (mechanical):
    Each reproducer produces REACHED-SINK EVIDENCE via ONE channel, recorded in repro_hints:
       (a) script/source harness -> write the exact bytes MANTIS_REACHED_ENTRYPOINT to a
           sidecar file $SENTINEL_FILE and flush+fsync (or unbuffered write) BEFORE
           invoking the sink. (A file survives a crash that truncates buffered stdout.)
       (b) binary / firmware / raw-payload -> reached-sink evidence is a captured
           crash backtrace or sanitizer frame (ASan/UBSan/MSan/TSan) that
           explicitly names the target sink function (target-produced tracing). A
           marker written by a wrapper you author BEFORE invoking the target is
           SETUP EVIDENCE ONLY: it proves "launch attempted," not "sink reached,"
           and does NOT qualify as reached-sink evidence. If no in-path marker
           (channel a) and no target-produced backtrace/sanitizer trace
           (channel b) is achievable, the sink is unreached.
     EVIDENCE PRESENT (reached-sink) = (channel a) sidecar file contains
       MANTIS_REACHED_ENTRYPOINT written in-path, OR (channel b) target-produced
       backtrace/sanitizer output names the sink. A wrapper pre-launch marker alone is
       NOT evidence present.
    EVIDENCE ABSENT includes: any compiler/build nonzero exit; exit 127 (command not
      found); exit 2 with a "No such file" message.
    DECISION GATE (gate the DECISION, not specific verdict strings):
      - Record repro_status = reproduced OR statically_confirmed ONLY if EVIDENCE is
        PRESENT. If ABSENT -> repro_status = not_attempted (retry-eligible), STOP.
      - In patch verification, EVIDENCE is required on the UNPATCHED baseline (Block G),
        NOT on the post-patch attack run (a correct patch legitimately stops the input
        before the sink).
      - If NO evidence channel is achievable for this target, downgrade to
        not_attempted / VERIFICATION_INCOMPLETE. NEVER synthesize the marker.
    

    具体的には、その実行が reached-sink の証拠を何も生成しなかった場合(ビルド/セットアップエラー、終了コード 127、「No such file」、または sink に一度も到達しなかった場合)、repro_status = not_attempted(再試行可能)を記録してください — 決して failed_to_reproduce にしてはいけません。また --reattack モードでは、reattack_status を未設定のままにし、履歴ノートに「setup_failed」を残してください — 決して failed_to_bypass にしてはいけません。ネガティブとして分類してよいのは、ハーネスが脆弱なエントリポイントに到達したことが証明でき、かつバグが発火しなかった場合だけです。

    HALT 上限(3状態ルール): state に active_snapshot が存在するが snapshot_pinned が false の場合(HALT モード — ツリーが競合したか、ピン留めできなかった場合)、failed_to_reproduce または failed_to_bypass を一切記録してはいけません。HALT では、コードがドリフトしている可能性があり、ネガティブな再現結果は権威あるものとして信頼できません。代わりに、repro_status = not_attempted(再試行可能)を記録し、--reattack モードでは、reattack_status を未設定のままにして、履歴ノートに「HALT mode: snapshot unpinned, negative result suppressed」を残してください。これは、HALT のすべてのステージに適用される権威ある判定の禁止を反映したものです。(MODE-OFF の場合 — active_snapshot がない場合 — は、今日と同様にネガティブを通常どおり分類してください。)

    • ロジック & 認可のバグ: 成功した再現スクリプトとは、ロジックの失敗を明示的に実証する機能的な単体テストまたはスクリプトです(例: 認可されていないリクエストが 200 OK を返す、またはテストスクリプトが検証を正常にバイパスして終了コード 0 で終了する)。
    • メモリ安全性 & バイナリクラッシュ: サンドボックス実行が stdout/stderr にクラッシュシグナルまたはサニタイザトレースを生成した場合、その再現を "reproduced" としてマークしてください。終了コードに関係なく、stdout/stderr をサニタイザのシグネチャについてスキャンしてください — UBSan はデフォルトで recover モード(終了コード 0)になるため、終了コード 0 の実行でも、バグが発火したことを証明する有効な UBSan トレースを含んでいる可能性があります。以下を確認してください:
      • AddressSanitizer(ASan)のエラー出力(例: ERROR: AddressSanitizer)。
      • UndefinedBehaviorSanitizer(UBSan)のランタイムレポート(例: runtime error:、SUMMARY: UndefinedBehaviorSanitizer)。
      • MemorySanitizer(MSan)のエラー出力(例: WARNING: MemorySanitizer)。 MSan の証拠が有効なのは、repro_hints に完全に計測された MSan ビルドが利用可能だったことが記録されている場合のみです(ステップ 3 の注意点)。完全な計測なしの素朴な -fsanitize=memory ビルドは、偽のトレースを生成します — これらを "reproduced" として分類しないでください。
      • ThreadSanitizer(TSan)の競合レポート(例: WARNING: ThreadSanitizer)。
      • セグメンテーションフォールト(SIGSEGV、終了コード 139)。
      • アボートシグナル(SIGABRT、終了コード 134)。
      • クラッシュまたはコアダンプ。
  3. トークン最適化されたファイル更新: LLM の出力トークンを最小限に抑えるため、出力内で JSON オブジェクト全体を再出力したり手動で書き換えたりしないでください。 代わりに、インプレース編集ツール(お好みの言語の短いスクリプトや jq など)を使用して、新しいフィールドを既存の state_root/workspace/findings/<id>.json ファイルにプログラムで追記してください。

    さらに、プランナーが試行を効率的に追跡できるよう、再現試行キャッシュを更新する必要があります:

    • state_root/workspace/archive/.repro_attempts.json に JSON キャッシュファイルを保持します。キャッシュファイルを作成、読み取り、またはロックする前に、親ディレクトリ state_root/workspace/archive/ が存在することを確認してください(例: mkdir -p state_root/workspace/archive/)。
    • キャッシュのキーには、UUID が再生成された場合でもループ実行をまたいで永続する安定した識別子を使用します。検出項目に signature フィールドがある場合は、それをそのままキャッシュキーとして使用します(これはすでに決定論的なコンテンツ同一性ハッシュです)。signature が存在しない場合は、検出項目の正規化されたタイトルとそのプライマリファイルパスを使用して計算した安定キーにフォールバックします: stable_key = normalized_title + "@" + primary_file_path。
      • normalized_title は、タイトルを小文字に変換し、すべての英数字以外の文字を削除して計算します。
      • primary_file_path は、code_paths の最初のエントリを取得し、行番号のサフィックスを除去して計算します(例: src/auth.c:120 を src/auth.c に変換)。
      • スナップショット対応キャップ(値の形状 {count, last_snapshot}): 各キャッシュ値をオブジェクト {count, last_snapshot} として保存します。値 V を読み取る際: V が単なる整数の場合は count=V、last_snapshot=UNKNOWN として扱います。V がオブジェクトの場合は V.count / V.last_snapshot を使用します(デフォルトは UNKNOWN)。インクリメントする前に、検出項目のスナップショットを現在の SNAPSHOT_ID と比較するブロック B を実行します: count=0 にリセットするのは、ブロック B が NOT_MATCHED であり、かつその理由が2つのスナップショットが存在し実際に異なることである場合のみです(本物のコード変更は新しい予算を獲得します)。UNKNOWN(不在/pass_/未ピン留め)の場合はリセットしないでください — そうすると VCS のないターゲットが永遠に再試行され続けます。さらに、決してリセットされない検出項目 ID ごとの絶対試行カウンターを保持し、スナップショットの変更にかかわらず、ハード上限(例: 6)に達したら再試行を停止します。
    • 同時実行中の競合状態(アトミックなファイル置換によるロックのバイパスを含む)を防ぎ、ロックレスリーダーを保護するため:
      • 決して削除も置換もされない専用の別個のロックファイル state_root/workspace/archive/.repro_attempts.lock を使用します。
      • このロックファイルに対して Python の fcntl.flock を使用して更新をアトミックに実行します:
      • ロックファイル state_root/workspace/archive/.repro_attempts.lock を開き(存在しない場合は作成)、コンテキストマネージャー(with 文)内で排他ロック(fcntl.LOCK_EX を指定した fcntl.flock)を取得します。

      • キャッシュファイル state_root/workspace/archive/.repro_attempts.json の現在の内容を読み取ります(存在しないか空の場合は {} として扱います)。

      • この検出項目のキャッシュキーエントリ(signature があればそれをキーとし、なければ stable_key。上記で定義した同じキー選択({count, last_snapshot} オブジェクト))の count フィールドを、Tier-3(完全なエンドツーエンドのサンドボックス化されたサービス)実行の場合にのみ 1 インクリメントします。内部の Tier-1 および Tier-2 のステッピングストーン試行は count をインクリメントしてはなりません(これらはステップ 3 のサブ予算ルールによって管理されます)。

      • 更新された JSON を同じディレクトリ内の一時ファイル(例: state_root/workspace/archive/.repro_attempts.json.tmp)に書き込みます。

      • 一時ファイルで対象のキャッシュファイルをアトミックに置換し(例: Python の os.replace)、リーダーが切り詰められたファイルや不完全なファイルを決して見ないようにします。

      • ロックファイルのファイルディスクリプタを閉じてロックを解放します(with コンテキストマネージャーを抜けると自動的に処理されます)。

    --reattack フラグが指定されているかどうかに応じて:

    • 通常どおり実行する場合(--reattack フラグなし): 既存のオブジェクトに対して以下を追加または更新する必要があります:

      • "repro_status"("reproduced"、"statically_confirmed"、"not_attempted"、または "failed_to_reproduce")。

      • "repro_file_path"

      • "run_command"

      • "repro_output"

      • "repro_snapshot_id": この実行が対象とした現在の SNAPSHOT_ID。

      • "repro_hints": コンパイルおよびサンドボックス実行のテレメトリを記録します (例: sanitizers_used: ASan+UBSan、assertions_disabled: true、 build_profile: release)。これは /mantis-critic に対する実証的な実行証拠を提供します。

      • 再現が成功した場合(repro_status が "reproduced" または "statically_confirmed" と評価される)かつ、所見の現在の "status" が "PROVISIONALLY_VALID" である場合: アップグレードする前に、所見の triage_checklist(存在する場合)をスキャンしてください。いずれかのエントリの outcome == "UNKNOWN" (または passes == false)がある場合、アップグレードしないでください: status は "PROVISIONALLY_VALID" のままにし、それでも repro_status を成功値に設定し (再現は実際に成功しています)、履歴ノート upgrade-to-VALID-blocked: triage_checklist has UNKNOWN entries (re-review required) を追加してください。 これはスキーマの VALID ⇒ no UNKNOWN allOf ゲート (schema.json 471-507 行)への違反を回避します。このゲートは、任意の VALID な非チェーン所見の triage_checklist において UNKNOWN/passes:false を禁止しています。reproduce は triage_checklist のエントリに触れません(チェックリストは review の成果物であり、 UNKNOWN エントリを解決できるのは review のみです)。triage_checklist が 存在しない場合(reviewer 履歴エントリがない場合、たとえばレガシーな所見)、または UNKNOWN/passes:false のエントリが 1 つもない場合、"status" を "VALID" に更新する必要があります。

      • "history" 配列へのエントリ:

      {
        "stage": "reproduce",
        "action": "reproduced",
        "details": "Reproduction status evaluated as [reproduced/failed_to_reproduce] using command: [run_command]",
        "pass_number": <current_pass_number>,
        "timestamp": "<current_iso8601_timestamp>"
      }
      
    • --reattack 付きで実行した場合: 既存のオブジェクトに対して以下の追加または更新を 行う必要があります(repro_* や status には触れないでください):

      • "reattack_status" ("bypassed_patch"、"failed_to_bypass"、 "inconclusive_baseline_changed")。

      • "bypassed_patch": PoC がパッチをバイパスし、バグをトリガーしました。 patch_status が VERIFIED_SECURE の場合、同じ書き込みでアトミックに patch_status = "VERIFICATION_FAILED" を設定します(バイパスはパッチを無効化します)。 これは「status に触れない」に対する明示的な例外であり、C5 ステップ 3 のパスと 同一スナップショット実行の両方に適用されます。

      • "failed_to_bypass": PoC は実行されましたが、パッチのバイパスに失敗しました。(ダウングレードは 不要です — failed_to_bypass は allOf ゲートが VERIFIED_SECURE に対して 要求する値です。)

      • "inconclusive_baseline_changed": パッチ未適用のベースラインが再実行され(以下 C5 を参照)、 バグが現在のパッチ未適用コードではもはやトリガーされなくなりました。 failed_to_bypass を主張しないでください。patch_status が VERIFIED_SECURE の場合、 同じ書き込みでアトミックに patch_status = "VERIFICATION_INCOMPLETE" を設定します。

      • INV-1(単一の情報源 — スキーマの VERIFIED_SECURE allOf ゲート): VERIFIED_SECURE => reattack_status は failed_to_bypass でなければならず、 failed_to_bypass には、パッチ適用済みシャドウ上でバグをトリガーすることに すべて失敗した ≥ 3 個の有効なバリアント入力(ステップ 3a)を持つ空でない reattack_variants 配列が必要です。 空または不足したバリアントセットは「すべて失敗」を空虚に真にしてしまいます — これは禁止されています: 代わりに VERIFICATION_INCOMPLETE で上限を設けてください。その他の結果は、同じ書き込みで patch_status をアトミックにダウングレードする必要があります — inconclusive_baseline_changed または不十分なバリアントセットの場合は VERIFICATION_INCOMPLETE、 bypassed_patch の場合は VERIFICATION_FAILED です。failed_to_bypass 以外の reattack_status とともに VERIFIED_SECURE を永続化しないでください。

      • スナップショット不一致 -> 以下 C5 に従います: reattack_snapshot_id != repro_snapshot_id の場合、独立して判定を記録しないでください — C5 に従います。C5 は CURRENT スナップショット上で未パッチのベースラインを再確立します。C5 を実行できない場合は、reattack_status を UNSET のままにし、SNAPSHOT_MISMATCH の履歴ノートを残すことにフォールバックします。

      • C5 — 未パッチベースラインの再実行(フェーズ 2): パッチ適用済みビルドに対して攻撃を実行する前に、reattack_snapshot_id != repro_snapshot_id(HALT モードの live: ツリーではない、真のスナップショット変更)である場合、まず未パッチのベースラインを再確立します:

        1. 現在のスナップショットの新規な未パッチコピーに対して再現プログラムを実行します — active_snapshot.root の新規 mktemp -d コピーであり、パッチ適用済みシャドウではありません。(これは mantis-patch のブロック G ステップ 1 が実行するのと同じ未パッチベースラインです。reproduce にはブロック G がありません。)
        2. 未パッチベースラインがトリガーしない場合(ブロック F に従い証拠が存在しない): reattack_status = "inconclusive_baseline_changed" を設定し、攻撃を続行せず、failed_to_bypass を主張しないでください。それでも reattack_file_path、reattack_run_command、reattack_output にベースライン再実行の詳細を必ず設定する必要があります。INV-1 を適用します(VERIFIED_SECURE -> VERIFICATION_INCOMPLETE にダウングレード)。
        3. 未パッチベースラインがトリガーする場合: 攻撃を続行します(bypassed_patch または failed_to_bypass)。INV-1 を適用します(bypassed_patch の場合は VERIFIED_SECURE -> VERIFICATION_FAILED にダウングレード)。
        • サニタイザーの一貫性: ベースライン再実行、攻撃、再攻撃は、元のベースラインをトリガーさせたのと同じサニタイザーフラグを使用しなければなりません。ベースラインが UBSan 経由でトリガーした場合(終了コード 0)、UBSan なしで攻撃を再コンパイルするとバグが隠蔽され、偽陰性(INV-2)または偽の failed_to_bypass(INV-1)を生じさせます。
        • HALT モードのガードレール: HALT は状態内の PASS のスナップショット ID(active_snapshot.snapshot_id が live: で始まる)で検出し、--snapshot_pinned=false 引数では検出しません(それはパッチ適用済みシャドウのためのセンチネル例外にすぎません)。HALT では C5 をスキップし、既存の HALT 上限に従います(reattack_status を UNSET のままにし、HALT ノートを残します)。
        • これは VERIFIED_SECURE ゲートを変更しません — ブロック G は依然として未パッチベースラインがトリガーすることを要求します。
      • "reattack_file_path"

      • "reattack_run_command"

      • "reattack_output"

      • "reattack_variants": ステップ 3a で試行されたバリアント入力ごとに 1 つのオブジェクトからなる配列。各オブジェクトは、必須キーである "description"(文字列)と "triggered"(真偽値)の 2 つを必ず含まなければなりません。自由形式やカスタムのキー(input、result、bypassed、label、name など)を考案しないでください:

        "reattack_variants": [
          {
            "description": "off-by-one: len=bound+1",
            "triggered": false
          },
          {
            "description": "alternate path via /api/v2/echo",
            "triggered": false
          },
          {
            "description": "boundary mutation: max INT_MAX",
            "triggered": false
          }
        ]
        
        • "description": そのバリアントが何を行うか(例: "off-by-one: len=bound+1"、"alternate path via /api/v2/echo")。
        • "triggered": boolean — このバリアントが、パッチを適用したシャドウ上で 元の脆弱性クラスをトリガーしたかどうか(ステップ 3a のバリアント 妥当性ガードレールに準拠)。無関係なクラッシュやエラーを引き起こした 無効・ジャンクなミュータントは、triggered = false として記録し、 無効である旨を description に記載します。reattack_status = "failed_to_bypass" の場合、 reattack_variants は非空(3 件以上のエントリ)でなければなりません(MUST)。 空の配列 [] は、failed_to_bypass 以外のステータス (例: バリアント探索の前に停止した C5 ベースライン失敗、または inconclusive_baseline_changed)としか共存できません。
      • "reattack_snapshot_id": この実行が対象とした現在の SNAPSHOT_ID。

      • "history" 配列へのエントリ:

      {
        "stage": "reattack",
        "action": "reproduced",
        "details": "Re-attack status evaluated as [bypassed_patch/failed_to_bypass] using command: [reattack_run_command]",
        "pass_number": <current_pass_number>,
        "timestamp": "<current_iso8601_timestamp>"
      }
      
  4. 再現の妥当性に対する批判: 再現が報告された脆弱性の再現例として妥当であることを 確実にするため、新しいコンテキストウィンドウを持つサブエージェントに生成された PoC をレビューさせ、批判させます。後になって虚偽の報告が表面化することが 決してないよう、本物の批判を求めます。

    バリアントへの批判(再攻撃のみ): --reattack が指定された場合、批判担当の サブエージェントは、バリアントの入力(ステップ 3a)が真に多様であること — 些細に 同一のミューテーション(例: 同じペイロードを保ったままコメントを変更する)でない こと — も検証しなければなりません(MUST)。批判担当がバリアントは意味のある形で 多様でないと判断した場合、履歴ノート variant_diversity_insufficient を記録し、 reattack_status を確定する前にバリアントを再作成します。

完了したら、ユーザーに通知します。

コメント