n7n-plugin-host 0.1.5

wasmtime-бекенд для crate::registry::PluginRegistry (n7n-harness) — друга (wasm-компонентна) реалізація слотів detector/verify/ast_facts/t0, поруч з in-process-бекендом §3.12 пункту 16а
Documentation
//! ЄДИНЕ місце в крейті, де guest отримує (чи не отримує) права на диск —
//! §3.9: "читання — read-only preopen кореня репозиторію". Кожен із
//! чотирьох `Wasm*::call*` (`detector.rs`/`verify.rs`/`ast_facts.rs`/
//! `t0.rs`) будує `Store` для guest-виклику РІВНО через
//! [`read_only_repo_ctx`] — жодної іншої точки конструювання `WasiCtx` у
//! крейті немає, тож "мутуючий guest" не потребує домовленості чи ревʼю:
//! щоб він з'явився, хтось мусив би змінити ЦЮ функцію (видно в diff).

use std::path::Path;

use wasmtime_wasi::{FsPerms, WasiCtx, WasiCtxBuilder};

/// Read-only `WasiCtx` з одним preopen — коренем репозиторію спроби,
/// змонтованим у guest-і як `/`.
///
/// `FsPerms::ReadOnly` — це ВЕСЬ бюджет прав, який [`WasiCtxBuilder`] тут
/// отримує; `FsPerms::ReadWrite` ніде в цій функції (і ніде більше в
/// крейті) не згадується.
///
/// До `wasmtime-wasi` 48 права були двома бітовими наборами
/// (`DirPerms::READ` + `FilePerms::READ`, і окремо існував
/// `DirPerms::MUTATE`); 48 звів їх до одного enum-а з двома варіантами.
/// Для цього крейта заміна СИЛЬНІША за попередній стан: раніше «нічого не
/// мутує» треба було вичитувати з комбінації двох наборів прапорців, тепер
/// це один літерал, який або `ReadOnly`, або ні — і третього стану, який
/// хтось міг би зібрати неуважною комбінацією бітів, більше не існує.
///
/// Наслідок незмінний: guest, що намагається
/// писати чи створювати/видаляти файл через `wasi:filesystem` (а саме цим
/// шляхом іде звичайний `std::fs::write` у скомпільованому для
/// `wasm32-wasip2` guest-і — доккоментар `detector.rs`, тест
/// `write_attempt_is_denied_by_the_read_only_preopen`), отримує помилку від
/// самого wasmtime-wasi ще до того, як його код побачив би файлову систему
/// — заборона за побудовою, не за угодою застосунку.
///
/// Хостовий `host-fs.read-file` (`crate::host_state`) — ДРУГИЙ, вужчий
/// шлях читання (доккоментар `wit/host.wit`), який іде НЕ через цей
/// `WasiCtx`, а напряму host-side `std::fs`; він так само ніколи не пише
/// (сигнатура `host-fs` не має жодної функції запису), тож обидва шляхи
/// читання, які §3.9 називає, лишаються read-only незалежно один від
/// одного.
pub(crate) fn read_only_repo_ctx(repo_root: &Path) -> wasmtime::Result<WasiCtx> {
    let mut builder = WasiCtxBuilder::new();
    builder.preopened_dir(repo_root, "/", FsPerms::ReadOnly)?;
    Ok(builder.build())
}

#[cfg(test)]
mod tests {
    //! Санітарний тест без `.wasm` (доккоментар `crate`-level "Тестовий
    //! guest", сценарій 2): підтверджує лише, що конструктор компілюється
    //! й повертає `Ok` на реальному каталозі — САМ доказ безпеки (guest не
    //! може писати) даний наскрізним тестом
    //! `tests/wasmtime_backend.rs::detector_end_to_end_denies_write_...`,
    //! бо перевірити ефект `FsPerms` можна лише реальним guest-викликом
    //! через `wasi:filesystem`, не інтроспекцією `WasiCtx` (тип не
    //! виставляє прав публічно).
    use super::*;

    #[test]
    fn read_only_repo_ctx_builds_successfully_on_a_real_directory() {
        let dir = tempfile::tempdir().expect("tempdir");
        if let Err(err) = read_only_repo_ctx(dir.path()) {
            panic!("{err}");
        }
    }
}