-
Notifications
You must be signed in to change notification settings - Fork 897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Define syntax for escaping declarative config env var substitution #4375
base: main
Are you sure you want to change the base?
Changes from all commits
e7f6017
92391d9
760426a
20121c4
e50dbb6
2d9633f
c95c10c
a26c3ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,6 +75,29 @@ more non line break characters (i.e. any character except `\n`). If a referenced | |
environment variable is not defined and does not have a `DEFAULT_VALUE`, it MUST | ||
be replaced with an empty value. | ||
|
||
The `$` character is an escape sequence, such that `$$` in the input is | ||
translated to a single `$` in the output. The resolved `$` from an escape | ||
sequence MUST not be considered when matching input against the environment | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MUST NOT ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think (correct me if I'm wrong) this is a critical part of the mechanics: Assuming If the resolved
If instead, the resolve
In other words, without this, an parser might think that it can do the substitution in two separate passes. The first pass replacing |
||
variable substitution regular expression. For example, `$${API_KEY}` resolves | ||
to `${API_KEY}`, and the value of the `API_KEY` environment variable is NOT | ||
substituted. See table below for more examples. In practice, this implies that | ||
parsers consume the input from left to right, iteratively identifying the next | ||
escape sequence, and matching the content since the prior escape sequence | ||
against the environment variable substitution regular expression. | ||
|
||
For example, the pseudocode for processing input `$${FOO} ${BAR} $${BAZ}` | ||
where `FOO=a, BAR=b, BAZ=c` would resemble: | ||
|
||
* Identify escape sequence `$$` at index 0. Perform substitution | ||
against `input.substring(0, 0)=""` => `""` and append to output. Append `$` | ||
to output. Current output: `"$"`. | ||
* Identify escape sequence `$$` at index 15. Perform substitution | ||
against `input.substring(0+2, 15)="{FOO} ${BAR} "` => `"{FOO} b "` and append | ||
to output. Append `$` to output. Current output: `"${FOO} b $"`. | ||
* Reach end of input without escape sequence. Perform substitution | ||
against `input.substring(15+2, input.length)="{BAZ}"` => `"{BAZ}"` and append | ||
to output. Return output: `"${FOO} b ${BAZ}"`. | ||
|
||
When parsing a configuration file that contains a reference not matching | ||
the references regular expression but does match the following PCRE2 | ||
regular expression, the parser MUST return an empty result (no partial | ||
|
@@ -94,55 +117,42 @@ example, see references to `INVALID_MAP_VALUE` environment variable below. | |
It MUST NOT be possible to inject environment variable by environment variables. | ||
For example, see references to `DO_NOT_REPLACE_ME` environment variable below. | ||
|
||
For example, consider the following environment variables, | ||
and [YAML](#yaml-file-format) configuration file: | ||
The table below demonstrates environment variable substitution behavior for a | ||
variety of inputs. Examples assume environment variables are set as follows: | ||
|
||
```shell | ||
export STRING_VALUE="value" | ||
export BOOL_VALUE="true" | ||
export INT_VALUE="1" | ||
export FLOAT_VALUE="1.1" | ||
export HEX_VALUE="0xdeadbeef" # A valid integer value written in hexadecimal | ||
export HEX_VALUE="0xdeadbeef" # A valid integer value (i.e. 3735928559) written in hexadecimal | ||
export INVALID_MAP_VALUE="value\nkey:value" # An invalid attempt to inject a map key into the YAML | ||
export DO_NOT_REPLACE_ME="Never use this value" # An unused environment variable | ||
export REPLACE_ME='${DO_NOT_REPLACE_ME}' # A valid replacement text, used verbatim, not replaced with "Never use this value" | ||
``` | ||
|
||
```yaml | ||
string_key: ${STRING_VALUE} # Valid reference to STRING_VALUE | ||
env_string_key: ${env:STRING_VALUE} # Valid reference to STRING_VALUE | ||
other_string_key: "${STRING_VALUE}" # Valid reference to STRING_VALUE inside double quotes | ||
another_string_key: "${BOOL_VALUE}" # Valid reference to BOOL_VALUE inside double quotes | ||
string_key_with_quoted_hex_value: "${HEX_VALUE}" # Valid reference to HEX_VALUE inside double quotes | ||
yet_another_string_key: ${INVALID_MAP_VALUE} # Valid reference to INVALID_MAP_VALUE, but YAML structure from INVALID_MAP_VALUE MUST NOT be injected | ||
bool_key: ${BOOL_VALUE} # Valid reference to BOOL_VALUE | ||
int_key: ${INT_VALUE} # Valid reference to INT_VALUE | ||
int_key_with_unquoted_hex_value: ${HEX_VALUE} # Valid reference to HEX_VALUE without quotes | ||
float_key: ${FLOAT_VALUE} # Valid reference to FLOAT_VALUE | ||
combo_string_key: foo ${STRING_VALUE} ${FLOAT_VALUE} # Valid reference to STRING_VALUE and FLOAT_VALUE | ||
string_key_with_default: ${UNDEFINED_KEY:-fallback} # UNDEFINED_KEY is not defined but a default value is included | ||
undefined_key: ${UNDEFINED_KEY} # Invalid reference, UNDEFINED_KEY is not defined and is replaced with "" | ||
${STRING_VALUE}: value # Invalid reference, substitution is not valid in mapping keys and reference is ignored | ||
recursive_key: ${REPLACE_ME} # Valid reference to REPLACE_ME | ||
# invalid_identifier_key: ${STRING_VALUE:?error} # If uncommented, this is an invalid identifier, it would fail to parse | ||
``` | ||
|
||
Environment variable substitution results in the following YAML: | ||
|
||
```yaml | ||
string_key: value # Interpreted as type string, tag URI tag:yaml.org,2002:str | ||
env_string_key: value # Interpreted as type string, tag URI tag:yaml.org,2002:str | ||
other_string_key: "value" # Interpreted as type string, tag URI tag:yaml.org,2002:str | ||
another_string_key: "true" # Interpreted as type string, tag URI tag:yaml.org,2002:str | ||
string_key_with_quoted_hex_value: "0xdeadbeef" # Interpreted as type string, tag URI tag:yaml.org,2002:str | ||
yet_another_string_key: "value\nkey:value" # Interpreted as type string, tag URI tag:yaml.org,2002:str | ||
bool_key: true # Interpreted as type bool, tag URI tag:yaml.org,2002:bool | ||
int_key: 1 # Interpreted as type int, tag URI tag:yaml.org,2002:int | ||
int_key_with_unquoted_hex_value: 3735928559 # Interpreted as type int, tag URI tag:yaml.org,2002:int | ||
float_key: 1.1 # Interpreted as type float, tag URI tag:yaml.org,2002:float | ||
combo_string_key: foo value 1.1 # Interpreted as type string, tag URI tag:yaml.org,2002:str | ||
string_key_with_default: fallback # Interpreted as type string, tag URI tag:yaml.org,2002:str | ||
undefined_key: # Interpreted as type null, tag URI tag:yaml.org,2002:null | ||
${STRING_VALUE}: value # Interpreted as type string, tag URI tag:yaml.org,2002:str | ||
recursive_key: ${DO_NOT_REPLACE_ME} # Interpreted as type string, tag URI tag:yaml.org,2002:str | ||
``` | ||
| YAML - input | YAML - post substitution | Resolved Tag URI | Notes | | ||
|-------------------------------------------|-----------------------------|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| | ||
| `key: ${STRING_VALUE}` | `key: value` | `tag:yaml.org,2002:str` | YAML parser resolves to string | | ||
| `key: ${BOOL_VALUE}` | `key: true` | `tag:yaml.org,2002:bool` | YAML parser resolves to true | | ||
| `key: ${INT_VALUE}` | `key: 1` | `tag:yaml.org,2002:int` | YAML parser resolves to int | | ||
| `key: ${FLOAT_VALUE}` | `key: 1.1` | `tag:yaml.org,2002:float` | YAML parser resolves to float | | ||
| `key: ${HEX_VALUE}` | `key: 0xdeadbeef` | `tag:yaml.org,2002:int` | YAML parser resolves to int `3735928559` | | ||
| `key: "${STRING_VALUE}"` | `key: "value"` | `tag:yaml.org,2002:str` | Double quoted to force coercion to string `"value"` | | ||
| `key: "${BOOL_VALUE}"` | `key: "true"` | `tag:yaml.org,2002:str` | Double quoted to force coercion to string `"true"` | | ||
| `key: "${INT_VALUE}"` | `key: "1"` | `tag:yaml.org,2002:str` | Double quoted to force coercion to string `"1"` | | ||
| `key: "${FLOAT_VALUE}"` | `key: "1.1"` | `tag:yaml.org,2002:str` | Double quoted to force coercion to string `"1.1"` | | ||
| `key: "${HEX_VALUE}"` | `key: "0xdeadbeef"` | `tag:yaml.org,2002:str` | Double quoted to force coercion to string `"0xdeadbeef"` | | ||
| `key: ${env:STRING_VALUE}` | `key: value` | `tag:yaml.org,2002:str` | Alternative `env:` syntax | | ||
| `key: ${INVALID_MAP_VALUE}` | `key: value\nkey:value` | `tag:yaml.org,2002:str` | Map structure resolves to string and _not_ expanded | | ||
| `key: foo ${STRING_VALUE} ${FLOAT_VALUE}` | `key: foo value 1.1` | `tag:yaml.org,2002:str` | Multiple references are injected and resolved to string | | ||
| `key: ${UNDEFINED_KEY}` | `key:` | `tag:yaml.org,2002:null` | Undefined env var is replaced with `""` and resolves to null | | ||
| `key: ${UNDEFINED_KEY:-fallback}` | `key: fallback` | `tag:yaml.org,2002:str` | Undefined env var results in substitution of default value `fallback` | | ||
| `${STRING_VALUE}: value` | `${STRING_VALUE}: value` | `tag:yaml.org,2002:str` | Usage of substitution syntax in keys is ignored | | ||
| `key: ${REPLACE_ME}` | `key: ${DO_NOT_REPLACE_ME}` | `tag:yaml.org,2002:str` | Value of env var `REPLACE_ME` is `${DO_NOT_REPLACE_ME}`, and is _not_ substituted recursively | | ||
| `key: ${STRING_VALUE:?error}` | n/a | n/a | Invalid substitution reference produces parse error | | ||
| `key: $${STRING_VALUE}` | `key: ${STRING_VALUE}` | `tag:yaml.org,2002:str` | `$$` escape sequence is replaced with `$`, `{STRING_VALUE}` does not match substitution syntax | | ||
| `key: $$${STRING_VALUE}` | `key: $value` | `tag:yaml.org,2002:str` | `$$` escape sequence is replaced with `$`, `${STRING_VALUE}` is replaced with `value` | | ||
| `key: $$$${STRING_VALUE}` | `key: $${STRING_VALUE}` | `tag:yaml.org,2002:str` | `$$` escape sequence is replaced with `$`, `$$` escape sequence is replaced with `$`, `{STRING_VALUE}` does not match substitution syntax | | ||
| `key: a $$ b` | `key: a $ b` | `tag:yaml.org,2002:str` | `$$` escape sequence is replaced with `$` | | ||
| `key: a $ b` | `key: a $ b` | `tag:yaml.org,2002:str` | No escape sequence, no substitution references, value is left unchanged | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: add changelog entry before merging