diff --git a/wfe-yaml/src/executors/deno/js/bootstrap.js b/wfe-yaml/src/executors/deno/js/bootstrap.js index 739b46f..8e7f198 100644 --- a/wfe-yaml/src/executors/deno/js/bootstrap.js +++ b/wfe-yaml/src/executors/deno/js/bootstrap.js @@ -2,6 +2,10 @@ globalThis.inputs = () => Deno.core.ops.op_inputs(); globalThis.output = (key, value) => Deno.core.ops.op_output(key, value); globalThis.log = (msg) => Deno.core.ops.op_log(msg); +globalThis.readFile = async (path) => { + return await Deno.core.ops.op_read_file(path); +}; + globalThis.fetch = async (url, options) => { const resp = await Deno.core.ops.op_fetch(url, options || null); return { diff --git a/wfe-yaml/src/executors/deno/ops/workflow.rs b/wfe-yaml/src/executors/deno/ops/workflow.rs index b3c05bc..de443fc 100644 --- a/wfe-yaml/src/executors/deno/ops/workflow.rs +++ b/wfe-yaml/src/executors/deno/ops/workflow.rs @@ -44,9 +44,29 @@ pub fn op_log(state: &mut OpState, #[string] msg: String) { tracing::info!(step = %name, "{}", msg); } +/// Reads a file from the filesystem and returns its contents as a string. +/// Permission-checked against the read allowlist. +#[op2] +#[string] +pub async fn op_read_file( + state: std::rc::Rc>, + #[string] path: String, +) -> Result { + // Check read permission + { + let s = state.borrow(); + let checker = s.borrow::(); + checker.check_read(&path) + .map_err(|e| deno_error::JsErrorBox::new("PermissionError", e.to_string()))?; + } + tokio::fs::read_to_string(&path) + .await + .map_err(|e| deno_error::JsErrorBox::generic(format!("Failed to read file '{path}': {e}"))) +} + deno_core::extension!( wfe_ops, - ops = [op_inputs, op_output, op_log, super::http::op_fetch], + ops = [op_inputs, op_output, op_log, op_read_file, super::http::op_fetch], esm_entry_point = "ext:wfe/bootstrap.js", esm = ["ext:wfe/bootstrap.js" = "src/executors/deno/js/bootstrap.js"], );