** Summary ** [AMG]: An understanding of double substitution is necessary to avoid [injection attack]s. Numerous commands interpret their arguments as scripts to be executed. `[[[eval]]` and `[[[subst]]` execute Tcl scripts. `[[[expr]]` (and therefore `[[[if]]` and `[[[while]]`) execute `[[[expr]]` scripts. This means that each of these commands take their arguments and parse them. These arguments were already parsed once prior to the invocation of the command, so the variable and command substitution that occurs during this new round of parsing interpretation is referred to as double substitution. Use braces all `[[[expr]]`essions, including `[[[if]]`, `[[[while]]`. Use a [static syntax analysis] tool to make sure you haven't missed any. ** See Also ** [Brace your expr-essions]: avoiding unintended double substitution ** Description ** [Dodekalogue%|%Rule 2] says that a Tcl command is evaluated in two steps. First, the Tcl interpreter breaks the command into words and performs substitutions. Then the interpreter uses the first word as a command name, calls the command, and passes the rest of the words to the command as arguments. By the time a command gets to see its arguments, Tcl has already performed its [Dodekalogue%|%various substitutions%|%] on those arguments. Many commands in trun perform their own substitutions on their arguments and/or pass those arguments back to the interperter to be evaluated scripts in their own right. The quintessential example is the [[[expr]]] command, but it is not the only one. Here are some other examples: `[[[after]]`: argument is a script to be evaluated later `[[[expr]]`: interprets `$` as variable substitution, `[` as command substition, etc. `[[[if]]`: second argument is interpreted according to `[[[expr]]` `[[[set]]`: interpets parenthesis as array element access `[[[list]]`: interprets `\` as backslash substitution, `"` and `{` as a grouping operators. Actally, this is also true of all the list commands `[[[eval]]`: interprets its arguments as entire Tcl scripts `[[[regexp]]` and [[[regsub]]]: interpret regular expression syntax in all its glory `[[[string match]]`: interprets `*`, `?`, `[`, and `\` `[[[trace]]`: argument is a script to be evaluated later `[[[while]]`: second argument is interpreted according to `[[[expr]]` Each of these commands can be thought of as a separate interpreter that implements its own mini-language. In the case of `[[[eval]]` and some others, the mini-language is just [Tcl] again, but anywhere a command is performing some sort of interpretation on some of the characters in its arguments, there are two layers of interpretation happening: Tcl performs its substitutions on command arguments first, and then the command may perform its own substitutions. Essentially, a script is being composed and then evaluated at runtime. This is a natural part of the design of Tcl, but if done incorrectly, it can leave one vulnerable to [Injection Attack%|%injection attacks], so it is important to understand and be aware of command substitution behaviour. This means understanding how each command you choose to use operates, and how it interprets its arguments. The [dodekalogue%|%standard rules of Tcl%|%] describe the syntax of Tcl, and each additional command documents its own parsing and interpretation behaviour. Arguments to `[[[expr]]` should should [brace your expr-essions%|%always be braced] because it avoids the first layer of substitution by the Tcl interpreter. The same is true for the first argument to '''[if]''' and '''[while]'''. This is mentioned on the [Tcl Style Guide] page and is discussed a bit on [A Question of Style]. Use a [static syntax analysis%|%syntax checker] to check for mistakes. Here is an example of a common mistake: ====== #warning: bad code ahead! set myString "hello" if "$myString eq {}" {puts "empty string"} ====== By the time `[[[if]]` sees its first argument, it looks like this: ====== hello eq {} ====== In expressions, strings must be quoted, and now `hello` is not, so an error occurs. `hello` was quoted for Tcl when it performed its substitutions, but it then was not quoted for `[[[expr]]` (via `[[[if]]`), which, requires that strings be enclosed in double quotes or braces. Note also that that the Tcl did not substitute away the curly brackets after `==`, since brace and double-quote grouping only happens when a brace or double quote occur at the beginning of a word ====== #warning: bad code ahead! set myString "hello there" if $myString=={} {puts "empty string"} ====== In this case, `[[[expr]]` gets the following value for its argument: ====== hello there=={} ====== which is even more of an error because now `[[[expr]]` can't make sense of the number of arguments *** A complex example *** In the following example, there are many issuses: ====== #warning: bad code ahead! set myString "This is a string with \[special characters\}" if $myString=={} {puts "empty string"} ====== `[[[expr]]` (via `[[[if]]`), sees the following value: ====== This is a string with [special characters}== ====== there are seven arguments, which makes no sense to `[[[expr]]`: * a left square bracket signaling command substitution, but no corresponding closing bracket * a right curly bracket signaling the end of a grouping operation, but no corresponding left curly bracket * seven arguments with only one operator to be found, which syntactically makes no sense to `[[[expr]]` The best course of action is to prevent the Tcl substitutions with curly braces: ====== if {$myString eq {}} {puts "empty string"} ====== Now `[[[expr]]` sees something more reasonable: ====== This\ is\ a\ string\ with\ \[special\ characters\} eq {} ====== ---- [KBK] - Another point that should be made is that braced expressions on `[[[if]]`, `[[[while]]` and `[[[expr]]` aren't just safer, they're also much, much faster. Unbraced ones have to be parsed at run time; braced ones can be compiled down to very tight [bytecode] sequences. <> String Processing