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:
2026-03-25 22:32:07 +00:00
parent ce68e4beed
commit 6fec7dbab5
15 changed files with 1127 additions and 66 deletions

View File

@@ -71,6 +71,24 @@ fn validate_steps(
}
}
// Deno steps must have config with script or file.
if let Some(ref step_type) = step.step_type
&& step_type == "deno"
{
let config = step.config.as_ref().ok_or_else(|| {
YamlWorkflowError::Validation(format!(
"Deno step '{}' must have a 'config' section",
step.name
))
})?;
if config.script.is_none() && config.file.is_none() {
return Err(YamlWorkflowError::Validation(format!(
"Deno step '{}' must have 'config.script' or 'config.file'",
step.name
)));
}
}
// Validate step-level error behavior.
if let Some(ref eb) = step.error_behavior {
validate_error_behavior_type(&eb.behavior_type)?;