rubyconfig/config

Unexpected formatting of ENV variable values

Closed this issue · 6 comments

Hello
We're using
ruby 2.5.7
gem 'config', '~> 3.1'

Config.setup do |config|
  config.const_name = 'Settings'
  config.use_env = true
  config.env_parse_values = false

  config.validation_contract = ConfigValidation.new
end

We have some env variable with the value of OpenSSL::PKey::EC format

settings.yml
ecdsa_key: "<%=ENV['ECDSA_KEY']%>

expected value
"-----BEGIN EC PRIVATE KEY-----\nXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=\n-----END EC PRIVATE KEY-----\n"

if I call it by Settings.ecdsa_key for some reason it reformats the value and removes \n:
"-----BEGIN EC PRIVATE KEY----- XXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX= -----END EC PRIVATE KEY----- "

making the key invalid.

Could anyone please guide me on how can to prevent such behavior?
Thank you in advance.

config will evaluate the YAML file as ERB first, then evaluate the result as YAML. This means that after ERB evaluation, if you want to use multiline strings, they must be encoded in a YAML-friendly way.

For example, your YAML starts off like this:

ecdsa_key: "<%= ENV['ECDSA_KEY'] %>"

Assuming ENV['ECDSA_KEY'] == "hello\nhi\n", after ERB evaluation, that ends up looking like

ecdsa_key: "hello
hi
"

config just takes the result of ERB evaluation and puts it through YAML.load, so if we load the resulting YAML file, we end up with a Hash that looks like

{"ecdsa_key"=>"hello hi "}

This is basically just because of how YAML string literals are defined to work.

To resolve your issue, I suggest either modifying your YAML file to format the resulting string as a YAML multiline string:

ecdsa_key: |-
<%= ENV['ECDSA_KEY'].gsub(/^/, " " * 2) %>

...which produces

ecdsa_key: |-
  hello
  hi

...and parses as

{"ecdsa_key"=>"hello\nhi"}

Or alternatively, just using the built-in support for ENV vars provided by config (there's no need to go from ENV -> YAML -> config since config can read ENV directly)

@cjlarose Thank you for the quick reply.
I'm afraid recovering the private key value using .gsub is not a reliable solution.
as for built-in support for ENV vars provided by config, how can I validate that the value is present with no use YAML?

how can I validate that the value is present with no use YAML?

If you'd like to validate that a value was specified for your ecdsa_key, I'd recommend adding a validation contract. Your contract can ensure that a value is specified.

@cjlarose Thanks for the advice
There is a contract, you can see in the given data above config.validation_contract = ConfigValidation.new

but the problem is the same if, by the reason of using ENV directly, as you have suggested, I do not put the key in the YAML files and at the same time add the presence validation to the validation contract, then the validation will always fail.
Do I understand you correctly you're suggesting using ENV directly and at the same time keep in ecdsa_key: "<%=ENV['ECDSA_KEY']%> in settings.yml just for the purpose of the validation?

Do I understand you correctly you're suggesting using ENV directly and at the same time keep in ecdsa_key: "<%=ENV['ECDSA_KEY']%>? settings.yml just for the purpose of the validation?

Not quite. If you're using ENV directly, it's not necessary to keep anything in the YAML files related to the ECDSA key. Given your configuration, you'd set the ENV var Settings.ecdsa_key or SETTINGS.ECDSA_KEY to your key. The validation contract is executed after loading the YAML files and reading ENV vars, so the validation contract should only fail if the ENV var was not specified in your case.

@cjlarose looks like it works, really appreciate your quick help, thank you!