動的評価
サンドボックス環境と制御されたモジュールアクセスにより、コードを実行時に動的に実行します。
2つのシステム
Section titled “2つのシステム”Wippyは2つの評価システムを提供しています:
| システム | 目的 | ユースケース |
|---|---|---|
expr | 式評価 | 設定、テンプレート、シンプルな計算 |
eval_runner | 完全なLua実行 | プラグイン、ユーザースクリプト、動的コード |
exprモジュール
Section titled “exprモジュール”expr-lang構文を使用した軽量な式評価。
local expr = require("expr")
local result, err = expr.eval("x + y * 2", {x = 10, y = 5})-- result = 20式のコンパイル
Section titled “式のコンパイル”一度コンパイルして何度も実行:
local program, err = expr.compile("price * quantity")
local total1 = program:run({price = 10, quantity = 5})local total2 = program:run({price = 20, quantity = 3})サポートされる構文
Section titled “サポートされる構文”-- 算術expr.eval("1 + 2 * 3") -- 7expr.eval("10 / 2 - 1") -- 4expr.eval("10 % 3") -- 1
-- 比較expr.eval("x > 5", {x = 10}) -- trueexpr.eval("x == y", {x = 1, y = 1}) -- true
-- ブールexpr.eval("a && b", {a = true, b = false}) -- falseexpr.eval("a || b", {a = true, b = false}) -- trueexpr.eval("!a", {a = false}) -- true
-- 三項演算子expr.eval("x > 0 ? 'positive' : 'negative'", {x = 5})
-- 関数expr.eval("max(1, 5, 3)") -- 5expr.eval("min(1, 5, 3)") -- 1expr.eval("len([1, 2, 3])") -- 3
-- 配列expr.eval("[1, 2, 3][0]") -- 1
-- 文字列連結expr.eval("'hello' + ' ' + 'world'")eval_runnerモジュール
Section titled “eval_runnerモジュール”セキュリティ制御付きの完全なLua実行。
local runner = require("eval_runner")
local result, err = runner.run({ source = [[ local function double(x) return x * 2 end return double(input) ]], args = {21}})-- result = 42| パラメータ | 型 | 説明 |
|---|---|---|
source | string | Luaソースコード(必須) |
method | string | 返されたテーブル内で呼び出す関数 |
args | any[] | 関数に渡す引数 |
modules | string[] | 許可される組み込みモジュール |
imports | table | インポートするレジストリエントリ |
context | table | ctxとして利用可能な値 |
allow_classes | string[] | 追加のモジュールクラス |
custom_modules | table | モジュールとしてのカスタムテーブル |
モジュールアクセス
Section titled “モジュールアクセス”許可されるモジュールをホワイトリスト化:
runner.run({ source = [[ local json = require("json") return json.encode({hello = "world"}) ]], modules = {"json"}})リストにないモジュールはrequireできません。
レジストリインポート
Section titled “レジストリインポート”レジストリからエントリをインポート:
runner.run({ source = [[ local utils = require("utils") return utils.format(data) ]], imports = { utils = "app.lib:utilities" }, args = {{key = "value"}}})カスタムモジュール
Section titled “カスタムモジュール”カスタムテーブルを注入します:
runner.run({ source = [[ return sdk.version ]], custom_modules = { sdk = {version = "1.0.0", api_key = "xxx"} }})コンテキスト値
Section titled “コンテキスト値”ctxとしてアクセス可能なデータを渡す:
runner.run({ source = [[ return "Hello, " .. ctx.user ]], context = {user = "Alice"}})プログラムのコンパイル
Section titled “プログラムのコンパイル”runner.compile はソースを検証し、実行せずにそのエントリポイントとモジュールを報告します:
local program, err = runner.compile([[ local function process(x) return x * 2 end return { process = process }]], "process", {modules = {"json"}})
program:method() -- "process" (string)program:modules() -- {"json"} (string[])コンパイル済みプログラムは情報提供用です。実行するには、ソースとメソッドを指定して runner.run を呼び出します。
セキュリティモデル
Section titled “セキュリティモデル”モジュールクラス
Section titled “モジュールクラス”モジュールは機能によって分類:
| クラス | 説明 | デフォルト |
|---|---|---|
deterministic | 純粋関数 | 許可 |
encoding | データエンコーディング | 許可 |
time | 時間操作 | 許可 |
nondeterministic | ランダムなど | 許可 |
process | spawn、レジストリ | ブロック |
storage | ファイル、データベース | ブロック |
network | HTTP、ソケット | ブロック |
ブロックされたクラスの有効化
Section titled “ブロックされたクラスの有効化”runner.run({ source = [[ local http = require("http_client") return http.get("https://api.example.com") ]], modules = {"http_client"}, allow_classes = {"network"}})権限チェック
Section titled “権限チェック”システムは以下の権限をチェック:
eval.compile- コンパイル前eval.run- 実行前eval.module- ホワイトリスト内の各モジュールeval.import- 各レジストリインポートeval.class- 各許可されたクラス
これらはセキュリティポリシーで設定します。
local result, err = runner.run({...})if err then if err:kind() == errors.PERMISSION_DENIED then -- セキュリティポリシーによりアクセス拒否 elseif err:kind() == errors.INVALID then -- 無効なソースまたは設定 elseif err:kind() == errors.INTERNAL then -- 実行またはコンパイルエラー endendユースケース
Section titled “ユースケース”プラグインシステム
Section titled “プラグインシステム”local plugins = registry.find({meta = {type = "plugin"}})
for _, plugin in ipairs(plugins) do local source = plugin:data().source runner.run({ source = source, method = "init", modules = {"json", "time"}, context = {config = app_config} })endテンプレート評価
Section titled “テンプレート評価”local template = "Hello, {{name}}! You have {{count}} messages."local compiled = expr.compile("name")
-- 高速な繰り返し評価for _, user in ipairs(users) do local greeting = compiled:run({name = user.name})endユーザースクリプト
Section titled “ユーザースクリプト”local user_code = request:body()
local result, err = runner.run({ source = user_code, modules = {"json", "text"}, -- 安全なモジュールのみ context = {data = input_data}})