コンテンツにスキップ

Plugin Trust and I/O Policy (slice 2)

ステータス:v0.1.0スライス(slice)2規範的。トラストモデルとプラグインが使用することが期待されるアナライザー側のI/Oサーフェス(surface)を固定します。拘束力のある設計サーフェスはADR-2 §「プラグイントラストとI/Oポリシー」です;このドキュメントがADRと矛盾する場合、ADRが優先されます。

ADR-2はスライス2の契約(contract)を3つの点を中心に固定します:

  1. プラグインはユーザー・Gemfile・.rigor.ymlが選択した信頼されたRuby gemです。スライス1のローダーはプラグインgemを設定に列挙することを要求することでこのトラスト境界をすでに強制します;スライス2はプラグインが従うことが期待される宣言的なポリシーを追加します。
  2. 決定論性のため、解析中はネットワークアクセスがデフォルトで無効です。
  3. ファイル読み込みはスコープが限定されており、プロジェクト・プロジェクトのRBSシグネチャ・アクティブなGemfile.lock・各信頼済みgemのGem::Specification#full_gem_pathのみが対象です。そのスコープ外の読み込みには明示的な設定とキャッシュ依存ディスクリプタが必要です。

ADR-2は強制的な隔離より文書化を明示的に選択しています:生のFile.readNet::HTTPで境界を迂回するプラグインはスライス2のスコープ外です。契約は、プラグインがアナライザー側の{Rigor::Plugin::IoBoundary}を使用する場合、その読み込みが検証され、ネットワーク呼び出しが拒否され、その入力が{Rigor::Cache::Descriptor}パイプラインを通じてキャッシュ無効化に使われることです。

パブリックネームスペース(ドリフト固定済み)

Section titled “パブリックネームスペース(ドリフト固定済み)”

以下の両クラスはspec/rigor/public_api_drift_spec.rbによって固定されています。

実行ごとのトラストスコープを記述する凍結値オブジェクト。

フィールド目的
trusted_gemsユーザーが承認したgemの名前のソート済み重複排除リスト。.rigor.ymlplugins:エントリーのgem名部分から導出される。
allowed_read_rootsプラグインが{IoBoundary}を通じて読み込める、ソート済みの絶対パス。デフォルト内容:プロジェクトルート(CWD)・すべてのsignature_pathsエントリー・各信頼済みgemのGem::Specification#full_gem_path・ユーザーがplugins_io.allowed_pathsに列挙した追加パス。
network_policy:disabled(デフォルト)または:allowlist(v0.1.2)。Configurationが受け入れる2つの値。
allowed_url_hostsnetwork_policy:allowlistのときにプラグインがフェッチできるホスト名の、ソート済み・重複排除・小文字化されたリスト。:disabledでは空(かつ無視される)。

述語:#allow_read?(path)(許可されたルートのいずれかに対する絶対パス包含チェック)・#network_allowed?(ポリシーが:allowlistのときのみtrue)・#allow_url?(url)(HTTPS + パース済みホストがallowed_url_hostsにある)・#gem_trusted?(name)#to_hは診断とキャッシュディスクリプタ用のシリアライズ可能なHashを返します。

{Rigor::Plugin::Services#io_boundary_for}によって構築されるプラグインごとのヘルパーサビス。凍結されたTrustPolicyと読み込みエントリーのインスタンスごとのアキュムレータを保持します。

メソッド目的
#read_file(path)絶対パスをポリシーに対して検証し、バイトを読み込み、:digestの{Cache::Descriptor::FileEntry}を境界の蓄積エントリーに追加します。拒否されたパスに対しては{Rigor::Plugin::AccessDeniedError}(reason: :read_outside_scope)を発生させます。
#open_url(url):disabledの下では{Rigor::Plugin::AccessDeniedError}(reason: :network_disabled)を発生させます。:allowlist(v0.1.2)の下では、パース済みホストがallowed_url_hostsにあるときHTTPS経由でGETを実行し、リクエストタイムアウト(10秒)とレスポンスボディサイズ上限(10 MB)を強制します;失敗時はreason::invalid_url_scheme:host_not_allowed:http_error:request_timeout:body_too_largeのいずれかのAccessDeniedErrorを発生させます。
#cache_descriptor境界が蓄積したFileEntry行を持つ新しい凍結された{Cache::Descriptor}を返します。後続の読み込みは基底レコードテーブルを拡張します;各呼び出しはその時点での読み込み履歴を反映した新しいディスクリプタを返します。

パスごとの読み込みは絶対パスによって重複排除されます;内容が変更されたファイルの再読み込みはエントリーのダイジェストを上書きします。

境界違反のパブリック例外。理由:

  • :read_outside_scoperead_fileがすべての許可読み込みルートの外のパスで呼び出された。
  • :network_disablednetwork_policy == :disabledの間にopen_urlが呼び出された。
  • :invalid_url_scheme / :host_not_allowed / :http_error / :request_timeout / :body_too_large:allowlistポリシーの下でのopen_url失敗(v0.1.2)。

問題のあるresource(パスまたはURL)を持ちます。

Rigor::Plugin::Services(トラスト + ファクトストアの追加)

Section titled “Rigor::Plugin::Services(トラスト + ファクトストアの追加)”

スライス2がトラストサーフェスを追加し;v0.1.1(ADR-9)がfact_storeを追加しました:

メソッド目的
#trust_policy実行の{TrustPolicy}。プロジェクトの.rigor.ymlからAnalysis::Runnerによって構築される。
#io_boundary_for(plugin_id)新しいプラグインごとの{IoBoundary}を返す。貢献マージャー(スライス3)は実行ごとにプラグインごとに1つを構築し、結果のキャッシュディスクリプタを組み込みプロデューサーと同じパイプラインに通す。
#fact_store実行ごとのクロスプラグイン{Rigor::Plugin::FactStore}(ADR-9 / v0.1.1)。プロデューサーは#prepare(services)で公開し、消費者は#diagnostics_for_file / #flow_contribution_forで読む。
plugins_io:
network: disabled # :disabled (default) or :allowlist (v0.1.2)
allowed_url_hosts: # required hostnames when network: allowlist
- example.com
allowed_paths: # extra read roots beyond project + sig + trusted gems
- vendor/generated
- db/schema.rb

Configuration#plugins_io_networkは解析されたシンボルを返します;Configuration#plugins_io_allowed_pathsはユーザーが指定した追加パスの凍結されたArray<String>を返します(相対パスはポリシー構築時にランナーが絶対パスに展開します)。

アナライザーの接続(Analysis::Runner

Section titled “アナライザーの接続(Analysis::Runner)”

スライス2のランナーは実行ごとに一度TrustPolicyを構築します:

  1. trusted_gems ← すべてのConfiguration#pluginsエントリーのgem名部分。
  2. allowed_read_roots
    • Dir.pwd(プロジェクトルート)。
    • すべてのConfiguration#signature_pathsエントリー(展開済み)。
    • 各信頼済みgemについて:Gem.loaded_specs[gem_name]&.full_gem_path(gemがロード可能な場合、失敗はサイレント——gemはインストール済みspecのないプロジェクトローカルである可能性があります)。
    • すべてのConfiguration#plugins_io_allowed_pathsエントリー(展開済み)。
  3. network_policyConfiguration#plugins_io_network:disabledデフォルト、またはConfiguration#plugins_io_allowed_url_hosts付きの:allowlist、v0.1.2)。

ポリシーはPlugin::Servicesに渡され、そこからすべてのプラグインのServices#io_boundary_for呼び出しに渡されます。境界を使用しないプラグインも文書化のためにservices.trust_policyを通じてポリシーを受け取ります。

スライス2が意図的に行わないこと

Section titled “スライス2が意図的に行わないこと”
  • 強制的な隔離。ADR-2はトレードオフを明示的に受け入れています:境界を迂回するプラグインはスコープ外です;スライス2の仕事は宣言的なポリシーと文書化されたエッジを提供することです。強力な隔離(Ruby::Box、プロセス境界)は将来のオプションであり、スライス2のコミットメントではありません。
  • realpathによるシンボリックリンクの解決File.expand_pathが唯一の正規化ステップです。敵対的なプラグインはスコープ外です。

(v0.1.2でネットワークゲートが解放されました: network_policy:allowlistも受け付けるようになり、IoBoundary#open_urlを通じてallowed_url_hosts内のホストへのHTTPS GETを、リクエストタイムアウトとレスポンスサイズ上限付きで許可します。デフォルトは:disabledのままです。)

  • 境界のキャッシュディスクリプタをCache::Storeに接続すること。それはスライス6の仕事です——プラグイン側キャッシュプロデューサーはPluginEntry行をディスクリプタスキーマに含むStore#fetch_or_compute(serialize:, deserialize:)を使用します。スライス2はディスクリプタを構築するだけです;まだ何もそれを消費しません。

© 2026 TypedDuck. Licensed under CC BY-SA 4.0.