Keep secrets safe in conditional branch rendering (#4353)
This commit is contained in:
@@ -4283,9 +4283,11 @@ class BranchEvaluationContext:
|
|||||||
*,
|
*,
|
||||||
workflow_run_context: WorkflowRunContext | None = None,
|
workflow_run_context: WorkflowRunContext | None = None,
|
||||||
block_label: str | None = None,
|
block_label: str | None = None,
|
||||||
|
template_renderer: Callable[[str], str] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.workflow_run_context = workflow_run_context
|
self.workflow_run_context = workflow_run_context
|
||||||
self.block_label = block_label
|
self.block_label = block_label
|
||||||
|
self.template_renderer = template_renderer
|
||||||
|
|
||||||
def build_template_data(self) -> dict[str, Any]:
|
def build_template_data(self) -> dict[str, Any]:
|
||||||
"""Build Jinja template data mirroring block parameter rendering context."""
|
"""Build Jinja template data mirroring block parameter rendering context."""
|
||||||
@@ -4402,28 +4404,36 @@ class JinjaBranchCriteria(BranchCriteria):
|
|||||||
criteria_type: Literal["jinja2_template"] = "jinja2_template"
|
criteria_type: Literal["jinja2_template"] = "jinja2_template"
|
||||||
|
|
||||||
async def evaluate(self, context: BranchEvaluationContext) -> bool:
|
async def evaluate(self, context: BranchEvaluationContext) -> bool:
|
||||||
# Build the template context explicitly to avoid surprises in templates.
|
# Prefer the renderer provided by the caller (matches block parameter rendering),
|
||||||
template_data = context.build_template_data()
|
# otherwise build a minimal sandboxed renderer using the evaluation context.
|
||||||
|
if context.template_renderer:
|
||||||
|
try:
|
||||||
|
rendered = context.template_renderer(self.expression)
|
||||||
|
except MissingJinjaVariables:
|
||||||
|
# Let upstream MissingJinjaVariables bubble as-is.
|
||||||
|
raise
|
||||||
|
except Exception as exc: # pragma: no cover - caught for robustness
|
||||||
|
raise FailedToFormatJinjaStyleParameter(self.expression, str(exc)) from exc
|
||||||
|
else:
|
||||||
|
template_data = context.build_template_data()
|
||||||
|
sandbox_env = (
|
||||||
|
SandboxedEnvironment(undefined=StrictUndefined)
|
||||||
|
if settings.WORKFLOW_TEMPLATING_STRICTNESS == "strict"
|
||||||
|
else SandboxedEnvironment()
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
template = jinja_sandbox_env.from_string(self.expression)
|
missing_vars = get_missing_variables(self.expression, template_data)
|
||||||
except Exception as exc:
|
if missing_vars:
|
||||||
raise FailedToFormatJinjaStyleParameter(
|
raise MissingJinjaVariables(self.expression, missing_vars)
|
||||||
template=self.expression,
|
|
||||||
msg=str(exc),
|
|
||||||
) from exc
|
|
||||||
|
|
||||||
if settings.WORKFLOW_TEMPLATING_STRICTNESS == "strict":
|
template = sandbox_env.from_string(self.expression)
|
||||||
if missing := get_missing_variables(self.expression, template_data):
|
rendered = template.render(template_data)
|
||||||
raise MissingJinjaVariables(template=self.expression, variables=missing)
|
except MissingJinjaVariables:
|
||||||
|
raise
|
||||||
try:
|
except Exception as exc:
|
||||||
rendered = template.render(template_data)
|
# Covers syntax errors and rendering issues
|
||||||
except Exception as exc:
|
raise FailedToFormatJinjaStyleParameter(self.expression, str(exc)) from exc
|
||||||
raise FailedToFormatJinjaStyleParameter(
|
|
||||||
template=self.expression,
|
|
||||||
msg=str(exc),
|
|
||||||
) from exc
|
|
||||||
|
|
||||||
return _evaluate_truthy_string(rendered)
|
return _evaluate_truthy_string(rendered)
|
||||||
|
|
||||||
@@ -4521,35 +4531,13 @@ class ConditionalBlock(Block):
|
|||||||
raise ValueError("organization_id is required to evaluate natural language branches")
|
raise ValueError("organization_id is required to evaluate natural language branches")
|
||||||
|
|
||||||
workflow_run_context = evaluation_context.workflow_run_context
|
workflow_run_context = evaluation_context.workflow_run_context
|
||||||
template_data = evaluation_context.build_template_data()
|
|
||||||
|
|
||||||
rendered_branch_criteria: list[dict[str, Any]] = []
|
rendered_branch_criteria: list[dict[str, Any]] = []
|
||||||
for idx, branch in enumerate(branches):
|
for idx, branch in enumerate(branches):
|
||||||
expression = branch.criteria.expression if branch.criteria else ""
|
expression = branch.criteria.expression if branch.criteria else ""
|
||||||
rendered_expression = expression
|
rendered_expression = (
|
||||||
|
evaluation_context.template_renderer(expression) if evaluation_context.template_renderer else expression
|
||||||
# Allow Jinja templating inside natural language branch expressions so users can
|
)
|
||||||
# mix free text with dynamic values (e.g., "If response: {{ foo.bar }}").
|
|
||||||
if "{{" in expression and "}}" in expression:
|
|
||||||
try:
|
|
||||||
template = jinja_sandbox_env.from_string(expression)
|
|
||||||
except Exception as exc:
|
|
||||||
raise FailedToFormatJinjaStyleParameter(
|
|
||||||
template=expression,
|
|
||||||
msg=str(exc),
|
|
||||||
) from exc
|
|
||||||
|
|
||||||
if settings.WORKFLOW_TEMPLATING_STRICTNESS == "strict":
|
|
||||||
if missing := get_missing_variables(expression, template_data):
|
|
||||||
raise MissingJinjaVariables(template=expression, variables=missing)
|
|
||||||
|
|
||||||
try:
|
|
||||||
rendered_expression = template.render(template_data)
|
|
||||||
except Exception as exc:
|
|
||||||
raise FailedToFormatJinjaStyleParameter(
|
|
||||||
template=expression,
|
|
||||||
msg=str(exc),
|
|
||||||
) from exc
|
|
||||||
|
|
||||||
rendered_branch_criteria.append({"index": idx, "expression": rendered_expression})
|
rendered_branch_criteria.append({"index": idx, "expression": rendered_expression})
|
||||||
|
|
||||||
@@ -4668,6 +4656,14 @@ class ConditionalBlock(Block):
|
|||||||
evaluation_context = BranchEvaluationContext(
|
evaluation_context = BranchEvaluationContext(
|
||||||
workflow_run_context=workflow_run_context,
|
workflow_run_context=workflow_run_context,
|
||||||
block_label=self.label,
|
block_label=self.label,
|
||||||
|
template_renderer=(
|
||||||
|
lambda potential_template: self.format_block_parameter_template_from_workflow_run_context(
|
||||||
|
potential_template,
|
||||||
|
workflow_run_context,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if workflow_run_context
|
||||||
|
else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
matched_branch = None
|
matched_branch = None
|
||||||
|
|||||||
Reference in New Issue
Block a user