feat(wfe-yaml): add deno_core JS/TS executor with sandboxed permissions
Secure JavaScript/TypeScript execution in workflow steps via deno_core, behind the `deno` feature flag. Security features: - Per-step permission system: net host allowlist, filesystem read/write path restrictions, env var allowlist, subprocess spawn control - V8 heap limits (64MB default) prevent memory exhaustion - Execution timeout with V8 isolate termination for sync infinite loops - Path traversal detection blocks ../ escape attempts - Dynamic import rejection unless explicitly enabled Workflow I/O ops: - inputs() — read workflow data as JSON - output(key, value) — set step outputs - log(message) — structured tracing Architecture: - JsRuntime runs on dedicated thread (V8 is !Send) - PermissionChecker enforced on every I/O op via OpState - DenoStep implements StepBody, integrates with existing compiler - Step type dispatch: "shell" or "deno" in YAML 34 new tests (12 permission unit, 3 config, 2 runtime, 18 integration).
This commit is contained in:
3
wfe-yaml/src/executors/deno/ops/mod.rs
Normal file
3
wfe-yaml/src/executors/deno/ops/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod workflow;
|
||||
|
||||
pub use workflow::{StepMeta, StepOutputs, WorkflowInputs};
|
||||
52
wfe-yaml/src/executors/deno/ops/workflow.rs
Normal file
52
wfe-yaml/src/executors/deno/ops/workflow.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
|
||||
/// Workflow data available to the script via `inputs()`.
|
||||
pub struct WorkflowInputs {
|
||||
pub data: serde_json::Value,
|
||||
}
|
||||
|
||||
/// Accumulates key/value outputs set by the script via `output(key, value)`.
|
||||
pub struct StepOutputs {
|
||||
pub map: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
/// Metadata about the currently executing step.
|
||||
pub struct StepMeta {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// Returns the workflow input data to JavaScript.
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_inputs(state: &mut OpState) -> serde_json::Value {
|
||||
let inputs = state.borrow::<WorkflowInputs>();
|
||||
inputs.data.clone()
|
||||
}
|
||||
|
||||
/// Stores a key/value pair in the step outputs.
|
||||
#[op2]
|
||||
pub fn op_output(
|
||||
state: &mut OpState,
|
||||
#[string] key: String,
|
||||
#[serde] value: serde_json::Value,
|
||||
) {
|
||||
let outputs = state.borrow_mut::<StepOutputs>();
|
||||
outputs.map.insert(key, value);
|
||||
}
|
||||
|
||||
/// Logs a message via the tracing crate.
|
||||
#[op2(fast)]
|
||||
pub fn op_log(state: &mut OpState, #[string] msg: String) {
|
||||
let name = state.borrow::<StepMeta>().name.clone();
|
||||
tracing::info!(step = %name, "{}", msg);
|
||||
}
|
||||
|
||||
deno_core::extension!(
|
||||
wfe_ops,
|
||||
ops = [op_inputs, op_output, op_log],
|
||||
esm_entry_point = "ext:wfe/bootstrap.js",
|
||||
esm = ["ext:wfe/bootstrap.js" = "src/executors/deno/js/bootstrap.js"],
|
||||
);
|
||||
Reference in New Issue
Block a user