{{name}} placeholder in the URL and the request body is replaced with the corresponding argument value. The encoding applied to that value depends on where in the request the placeholder appears.
Only the URL (SubstituteURL) and body (SubstituteBody) are templated. Header values are not — see Header values below.
This page documents the encoding rules and shows what each one prevents.
Encoding rules by location
The URL is split into a locked scheme + host prefix and a substitutable path/query/fragment suffix. The split happens on the first
://, then the first ? and #. The Content-Type lookup for the body is case-insensitive and ignores any ; charset=… suffix.
Scheme and host are locked
You cannot put a{{arg}} in the scheme or host of an HTTP button’s URL. The button service derives an allowed_host from the literal scheme + authority when the button is created, and rejects a URL whose scheme or host contains a placeholder. Placeholders are only ever expanded in the path, query, and fragment.
This is what stops a {{arg}} value sourced from an attacker (for example a value forwarded from a webhook body) from redirecting the request to a different host. If a hand-edited button.json somehow smuggles a placeholder into the scheme/host, the substitution step leaves it untouched and dispatch fails with a VALIDATION_ERROR rather than making the request.
See HTTP API buttons for the host-locking model in full.
Attack scenario 1: URL query injection
Template:real&admin=true
Without encoding:
&admin=true becomes a second query parameter that the server sees.
With QueryEscape:
& and = are percent-encoded. The server receives q = real&admin=true as a single value.
Attack scenario 2: URL path traversal
Template:../../etc/passwd
Without encoding:
PathEscape:
Attack scenario 3: JSON field injection
Template:","role":"admin
Without encoding:
role value.
With JSON string escaping:
json.Marshal and the outer quotes stripped, so the template’s own quotes stay in place.
Header values
Header values are not templated.{{arg}} placeholders inside a header value are not expanded — the header is sent exactly as stored in the button. A credential such as a token or API key is therefore set as a literal, static header value when the button is created, which is correct for values that encoding would break (e.g. a base64 credential containing + and =).
Because header values are static and never carry an argument, there is no per-press injection vector through them — but you are responsible for the value you store. Treat anything you put in a header as a committed part of the button spec.
Raw body types
For bodies with aContent-Type other than application/json / text/json (or no Content-Type at all, which is treated as JSON) and application/x-www-form-urlencoded, substitution is raw — no encoding is applied. Use this only when you fully control the body format.
Related
- URL and body templates — how templates work at a conceptual level
- HTTP API buttons — full flag reference and host-locking
- Security overview — threat model and assumptions