You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(condition): stop a secret value from breaking or forging a condition
Condition expressions pasted every environment variable value into the
expression as source. Block references in the same expression go through a
proper escape and get quoted; env vars went through neither. That left three
defects:
- A bare string placeholder was a SyntaxError. `{{NAME}} === 'alice'` resolved
to `alice === 'alice'`, so the form the Function block docs recommend could
not be used here at all.
- Ordinary data broke the block. An apostrophe (`O'Brien`) or a newline in a
legitimate value produced unparseable source and failed the run.
- The quoted form was injectable. A value of `x' || true || '` turned
`'{{NAME}}' === 'bob'` into `'x' || true || '' === 'bob'`, forging a true
branch out of a comparison that should be false.
Inline only structurally inert literals — numbers, booleans, and null, with
optional space/tab padding. Every other value keeps its `{{NAME}}` placeholder
and is bound as a string by the execution-boundary compiler, the same one
Function blocks and Custom Tools already use.
Legacy outcomes are preserved. `{{COUNT}} === 3` and `{{ENABLED}} === true`
still compare as literals, and an embedded `"Bearer {{API_KEY}}"` still
compares equal — now via compiled concatenation rather than a pasted value.
Padding is admitted rather than trimmed so the inlined text stays
byte-identical to the stored value, which is what keeps a padded number
correct both bare and quoted.
A resolved secret also no longer travels to the execution boundary inside the
condition source.
The one deliberate behavior change: a value whose text is itself a quoted JS
literal (a secret stored as `'foo'`, a plausible workaround for the bare-string
SyntaxError) now compares as the 5-character string rather than as source.
That form is the injectable one, so it cannot be kept.
Docs: state the placeholder type contract, which was described mechanically but
never in terms of what a reader gets. `{{KEY}}` in Function and Custom Tool code
always evaluates to a string, so a bare `if ({{FLAG}})` is always true and a list
has to be stored as JSON. This is what a customer hit after the resolver lift in
#6247 moved Function blocks off source inlining.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Read an [environment variable](/workflows/variables#environment-variables) with `{{KEY}}`:
35
+
36
+
```javascript
37
+
{{MAX_RETRIES}} ===3
38
+
{{FEATURE_ON}} ===true
39
+
{{TIER}} ==='pro'
40
+
```
41
+
42
+
Numbers, booleans, and `null` compare as literals. Every other value is bound as a string, so an apostrophe, quote, or newline inside a secret cannot change what the expression means.
43
+
34
44
<Callouttype="info">
35
45
If an expression throws, for example because it reads a field that is not there, the block errors and the run follows the [error path](/workflows/connections) if one is connected. Guard missing values with optional chaining (`?.`) or a null check.
Copy file name to clipboardExpand all lines: apps/docs/content/docs/en/workflows/blocks/function.mdx
+19Lines changed: 19 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -58,6 +58,25 @@ const apiKey = {{API_KEY}};
58
58
59
59
Existing quoted and embedded forms are also supported, including `"{{API_KEY}}"`, `"Bearer {{API_KEY}}"`, and placeholders in template literals. Function and Custom Tool code use the same compiler at the execution boundary. It binds the secret separately from the source instead of pasting plaintext into your code, so quotes, backslashes, newlines, and string values such as `"123"` and `"true"` retain their exact contents and do not become JavaScript or Python literals of another type.
60
60
61
+
### Placeholders are always strings
62
+
63
+
A placeholder is bound as a value, never parsed as source. That is what keeps a secret from running as code — but it also means `{{KEY}}` always evaluates to a **string**, whatever the value looks like. Convert it when your code needs another type:
64
+
65
+
```javascript
66
+
constretries=Number({{MAX_RETRIES}});
67
+
constenabled= {{FEATURE_ON}} ==='true';
68
+
constpatterns=JSON.parse({{PATTERN_LIST}});
69
+
```
70
+
71
+
In Python, use `int()`, `== "true"`, and `json.loads()` the same way.
72
+
73
+
Two cases are easy to miss:
74
+
75
+
-**Booleans.** A bare `if ({{FEATURE_ON}})` is always true, because the string `"false"` is truthy. Compare against `'true'` instead.
76
+
-**Lists and objects.** Store the value as JSON so you can parse it back. `["^[A-Z]{2}-\\d{4}$", "^\\d{7,15}$"]` becomes an array; a JavaScript array of regex literals does not. It arrives as one long string, and iterating it walks character by character.
77
+
78
+
[Condition](/workflows/blocks/condition) blocks differ in one way: a number, boolean, or `null` value compares as a literal there, so `{{MAX_RETRIES}} === 3` is true. Every other value is a string, exactly as it is here.
79
+
61
80
JavaScript regex literals can contain a placeholder:
Copy file name to clipboardExpand all lines: apps/docs/content/docs/en/workflows/variables.mdx
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -85,6 +85,8 @@ Reference them with double curly braces in any block field, including Agent syst
85
85
Environment variable names must start with a letter or underscore and contain only letters, numbers, and underscores, like `MY_API_KEY`.
86
86
</Callout>
87
87
88
+
Inside Function and Custom Tool code, `{{KEY}}` always evaluates to a **string** — the value is bound, never parsed as source. Use `Number()`, a `=== 'true'` comparison, or `JSON.parse()` when you need another type. See [secret placeholders in code](/workflows/blocks/function#placeholders-are-always-strings).
0 commit comments