diff --git a/README.md b/README.md index 307cf226..b28ba882 100644 --- a/README.md +++ b/README.md @@ -107,18 +107,14 @@ Redaction can also be configured on a per-project basis using a `.veil.toml` fil ### Example -```toml -# If APP_ENV = "dev" or APP_ENV = "qa"... -[[env.APP_ENV]] -values = ["dev", "qa"] -redact = false # don't redact data +`APP_ENV` is just an example here. You can match multiple environment variables with any UTF-8 name and value(s). -# If APP_ENV = "production" or APP_ENV = "staging"... -[[env.APP_ENV]] -values = ["production", "staging"] -redact = true # do redact data +```toml +[env.APP_ENV] +redact = ["production", "staging"] # redact data if "APP_ENV" is set to any of these values +skip-redact = ["dev", "qa"] # SKIP redacting data if "APP_ENV" is set to any of these values -# If APP_ENV isn't set or isn't recognised... +# If "APP_ENV" isn't set or isn't recognised... [fallback] redact = true # do redact data (default) # OR diff --git a/src/lib.rs b/src/lib.rs index a91fab31..4dc5749c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -220,16 +220,12 @@ //! //! ### Example //! -//! ```toml -//! ## If APP_ENV = "dev" or APP_ENV = "qa"... -//! [[env.APP_ENV]] -//! values = ["dev", "qa"] -//! redact = false # don't redact data +//! `APP_ENV` is just an example here. You can match multiple environment variables with any UTF-8 name and value(s). //! -//! ## If APP_ENV = "production" or APP_ENV = "staging"... -//! [[env.APP_ENV]] -//! values = ["production", "staging"] -//! redact = true # do redact data +//! ```toml +//! [env.APP_ENV] +//! redact = ["production", "staging"] # redact data if APP_ENV is set to any of these values +//! skip-redact = ["dev", "qa"] # SKIP redacting data if APP_ENV is set to any of these values //! //! ## If APP_ENV isn't set or isn't recognised... //! [fallback] diff --git a/veil-macros/src/env.rs b/veil-macros/src/env.rs index d58b97c8..593883f0 100644 --- a/veil-macros/src/env.rs +++ b/veil-macros/src/env.rs @@ -34,11 +34,14 @@ impl ToTokens for FallbackBehavior { #[derive(Deserialize)] /// Should we redact data based on the values of environment variables? struct EnvRedactConfig { - /// If the environment variable is set to one of these values... - values: Vec, + #[serde(default)] + /// Redaction should be ON if the environment variable is set to one of these values. + redact: Vec, - /// ...then we should [redact|not redact] the data. - redact: bool, + #[serde(default)] + #[serde(rename = "skip-redact")] + /// Redaction should be OFF if the environment variable is set to one of these values. + skip_redact: Vec, } #[derive(Deserialize)] @@ -59,13 +62,13 @@ impl Default for FallbackRedactConfig { struct TomlVeilConfig { #[serde(default)] fallback: FallbackRedactConfig, - env: Option>>, + env: Option>, } #[derive(Default)] pub struct VeilConfig { fallback: FallbackRedactConfig, - env: BTreeMap>, + env: BTreeMap, } impl VeilConfig { pub fn read(path: &Path) -> Result { @@ -75,17 +78,22 @@ impl VeilConfig { // Ensure there are no duplicate key-value environment variable pairs. if let Some(env) = &config.env { let mut pairs = Vec::new(); - for (key, configs) in env { - for config in configs { - for value in &config.values { - let pair = (key.as_str(), value.as_str()); - if pairs.contains(&pair) { - return Err(VeilConfigError::Custom(format!( - "duplicate key-value environment variable pair: {pair:?}" - ))); - } else { - pairs.push(pair); - } + for (key, config) in env { + // Ensure there are no empty environment variable configs. + if config.redact.is_empty() && config.skip_redact.is_empty() { + return Err(VeilConfigError::Custom(format!( + "Environment variable {key:?} has an empty configuration" + ))); + } + + for value in [&config.redact, &config.skip_redact].into_iter().flatten() { + let pair = (key.as_str(), value.as_str()); + if pairs.contains(&pair) { + return Err(VeilConfigError::Custom(format!( + "duplicate key-value environment variable pair: {pair:?}" + ))); + } else { + pairs.push(pair); } } } @@ -187,17 +195,20 @@ pub fn env_is_redaction_enabled(input: TokenStream) -> TokenStream { } }; - let env = config.env.iter().map(|(key, configs)| { - let values = configs.iter().map(|config| config.values.as_slice()); - let redacts = configs.iter().map(|config| config.redact); + let env = config.env.iter().map(|(key, config)| { + let redacts = &config.redact; + let skips = &config.skip_redact; quote! { if let Ok(value) = ::std::env::var(#key) { - #({ - static VALUES: &'static [&'static str] = &[#(#values),*]; - if VALUES.contains(&value.as_str()) { - return #redacts; - } - })* + static REDACTS: &[&str] = &[#(#redacts),*]; + if REDACTS.contains(&value.as_str()) { + return true; + } + + static SKIPS: &[&str] = &[#(#skips),*]; + if SKIPS.contains(&value.as_str()) { + return false; + } } } }); diff --git a/veil-tests/environment-aware/.veil.toml b/veil-tests/environment-aware/.veil.toml index 19a80d82..352e18e5 100644 --- a/veil-tests/environment-aware/.veil.toml +++ b/veil-tests/environment-aware/.veil.toml @@ -1,7 +1,3 @@ -[[env.APP_ENV]] -values = ["dev", "qa"] -redact = false - -[[env.APP_ENV]] -values = ["production", "staging"] -redact = true +[env.APP_ENV] +skip-redact = ["dev", "qa"] +redact = ["production", "staging"]