Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Keywords: #p20220707a #env #variable #contract #ruby #gem
Ruby approach in creating contracts for ENV variables.
Having a contract for ENV vars gives you a number of new benefits:
"on"
/"off"
to some ENV variables they require, "true"
/"false"
or "true"
/nil
to others, which makes working with ENV vars a poorly documented mess unless we have exposed it all in our contract).:deprecated
, :irrelevant
or :ignore
, leaving developers no question about the applicability of a particular variable.The larger your application, the more useful the ENV contract gets.
After installing this gem, define your contract:
EnvControl.configuration.contract = {
MY_UUID_VAR: :uuid,
...
}
Then validate the ENV variables only after they are all set (manually or by dotenv / Figaro):
EnvControl.validate(ENV)
If the contract has been breached, the #validate
method raises EnvControl::BreachOfContractError
exception. This behavior can be customized to suit your needs.
Consider the following example:
EnvControl.configuration.contract = {
ADMIN_EMAIL: :email,
DEBUG: ["true", nil],
LC_CTYPE: "UTF-8",
MY_VAR1: :string,
MY_VAR2: :bool,
MYSQL_DEBUG: :not_set, # same as nil
MYSQL_PWD: :deprecated,
RAILS_ENV: ["production", "development", "test"],
TMPDIR: :existing_file_path,
}
The contract is a list of ENV variables and validators you have attached to them.
Validators can be:
nil
, which literally means "we expect this variable to be unset".#call
method)It is allowed to mix validators of different types:
EnvControl.configuration.contract = {
# Allowed values: "true" OR "false" OR "weekly" OR "daily" OR "hourly" OR nil
MY_RETRY: [:bool, "weekly", "daily", "hourly", nil],
}
The EnvControl gem contains several built-in validators that you can use in your contracts.
Built-in validators are simply method names specified as symbols, e.g. :string
, :uuid
, :email
etc.
These methods take ENV variable as input argument and return 'true' or 'false' depending on its value.
List of built-in validators:
Validator | Acceptable values | Comments |
---|---|---|
:bool | "true" , "false" | |
:string | any non-empty string | " " considered empty |
:email | any e-mail address | |
:integer | any integer string | |
:hex | hexadecimal numbers | |
:ignore | nil / any string | Allows empty "" value |
:empty | nil or empty string | Same as [:not_set, ""] |
:irrelevant | nil / any string | Synonym for :ignore |
:deprecated | nil (not set) | Synonym for nil |
:not_set | nil (not set) | Synonym for nil |
:uri | any uri | |
:https_uri | any secure http uri | |
:postgres_uri | any postgres uri | |
:uuid | UUID string | |
:existing_path | file or folder path | Both files and dirs |
:existing_file_path | full file path | |
:existing_folder_path | full folder path |
You can create your own validators if needed.
Important: Validators only work with non-nil ENV variables. If the variable is not set (nil), the validator won't be called.
TODO
You can create your own validators. There are two approaches available.
Callable objects. Validators of this kind must respond to the #call
method, so they can be Proc
s, Lambda
s or custom objects.
class StrongPasswordValidator
def self.call(string)
string.match? A_STRONG_PASSWORD_REGEX
end
end
EnvControl.configuration.contract = {
DB_PASSWORD: [StrongPasswordValidator, :not_set],
}
Custom methods to extend EnvControl::Validators
module. These methods can reuse existing validators, making "AND" logic available to you:
module MyContractValidators
def irc_uri(string)
uri(string) && URI(string).scheme.eql?("irc")
end
end
EnvControl::Validators.extend(MyContractValidators)
EnvControl.configuration.contract = {
IRC_CHANNEL: :irc_uri,
...
}
gem install env_control
or add the gem to your Gemfile and then run bundle install
:
# Gemfile
gem "env_control"
EnvControl.configuration
is a configuration object that contains the default settings. You can set its attributes directly or within a block:
require "env_control"
EnvControl.configuration do |config|
config.environment_name = ...
config.contract = {...}
config.on_validation_error = MyContractErrorHander.new
end
EnvControl.validate(ENV)
Alternatively, you can provide/override contract using keyword attributes in #validate
method:
EnvControl.validate(
ENV,
environment_name: "review",
contract: contract,
on_validation_error: MyContractErrorHander.new,
)
TODO
TODO
TODO
TODO
TODO
Maintain the ENV contract up to date so that other developers can use it as a source of truth about the ENV variables requirements. Feel free to add comments to the contract.
Keep the contract keys alphabetically sorted or group the keys by sub-systems of your application.
Keep the contract as permissive as you can. Avoid putting sensitive string literals.
Some validators like :deprecated
are effectively equivalent to nil
. Give them preference when you need to accompany a requirement to have a variable unset with an appropriate reason.
Add :deprecated
to existing validators before proceeding to remove code that uses the variable., e.g.:
MY_VAR: [:deprecated, :string]
Consider defining "virtual" environments via environment_name=
without introducing them to the application. This may be useful if you, say, need to run your review app in "production" environment but with a more restricted ENV contract:
EnvControl.configuration do |config|
config.environment_name = \
if [ENV['RAILS_ENV'], ENV['REVIEW']] == ['production', 'true']
'review' # virtual production-like environment
else
ENV['RAILS_ENV']
end
config.contract = {
S3_BUCKET: {
"production" => :string,
"review" => "qa_bucket", # safe bucket
"default" => :not_set
}
}
end
FAQs
Unknown package
We found that env_control demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.