diff --git a/.drone.yml b/.drone.yml index 671eb7ea..bfea5026 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,5 +1,4 @@ ---- -kind: pipeline +--- kind: pipeline name: default platform: @@ -36,7 +35,7 @@ steps: - name: cargo-clippy-ci image: rust:1.61 commands: - - cargo clippy -- -D warnings && cargo clippy --features environment-aware -- -D warnings + - cargo clippy -- -D warnings && cargo clippy --features toggle -- -D warnings environment: BUILD_ENV: dev CARGO_HOME: /drone/src/.cargo @@ -46,7 +45,7 @@ steps: - name: cargo-test image: rust:1.61 commands: - - cargo test --all && cargo test --all --features environment-aware + - cargo test --all && cargo test --all --features toggle environment: BUILD_ENV: dev CARGO_HOME: /drone/src/.cargo diff --git a/Cargo.lock b/Cargo.lock index a7d5d1b7..3c45f9b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,24 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - [[package]] name = "glob" version = "0.3.0" @@ -41,28 +23,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.132" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" - -[[package]] -name = "lock_api" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "memchr" version = "2.5.0" @@ -71,32 +31,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "once_cell" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys", -] +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "proc-macro2" @@ -116,15 +53,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" version = "1.6.0" @@ -157,20 +85,11 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "serde" version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" -dependencies = [ - "serde_derive", -] [[package]] name = "serde_derive" @@ -194,12 +113,6 @@ dependencies = [ "serde", ] -[[package]] -name = "smallvec" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" - [[package]] name = "syn" version = "1.0.100" @@ -254,7 +167,7 @@ checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" name = "veil" version = "0.1.0" dependencies = [ - "lazy_static", + "once_cell", "veil-macros", ] @@ -262,12 +175,9 @@ dependencies = [ name = "veil-macros" version = "0.1.0" dependencies = [ - "lazy_static", "proc-macro2", "quote", - "serde", "syn", - "toml", ] [[package]] @@ -281,45 +191,13 @@ dependencies = [ ] [[package]] -name = "veil-tests-environment-aware" +name = "veil-tests-disable-redaction" version = "0.1.0" dependencies = [ - "parking_lot", "veil", "veil-tests", ] -[[package]] -name = "veil-tests-environment-aware-disable" -version = "0.1.0" -dependencies = [ - "veil", - "veil-tests", -] - -[[package]] -name = "veil-tests-environment-aware-fallback-off" -version = "0.1.0" -dependencies = [ - "veil", - "veil-tests", -] - -[[package]] -name = "veil-tests-environment-aware-fallback-on" -version = "0.1.0" -dependencies = [ - "veil", - "veil-tests", -] - -[[package]] -name = "veil-tests-environment-aware-fallback-panic" -version = "0.1.0" -dependencies = [ - "veil", -] - [[package]] name = "walkdir" version = "2.3.2" @@ -361,46 +239,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/Cargo.toml b/Cargo.toml index 20ffee6b..a8bb49a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,19 +7,16 @@ license = "MIT" repository = "https://github.com/primait/veil" [workspace] -members = [ - "veil-macros", - "veil-tests", - "veil-tests/environment-aware", - "veil-tests/environment-aware-fallback-on", - "veil-tests/environment-aware-fallback-off", - "veil-tests/environment-aware-fallback-panic", - "veil-tests/environment-aware-disable", +members = ["veil-macros", "veil-tests" +# Because those tests deal with global state we need to run them in a separate process. +# The easiest/only way to do this with the standard rust test harness is to put them in a +# separate crate +,"veil-tests/disable-redaction-test" ] -[features] -environment-aware = ["veil-macros/environment-aware", "lazy_static"] - [dependencies] veil-macros = { path = "veil-macros" } -lazy_static = { version = "1", optional = true } +once_cell = "1" + +[features] +toggle = [] diff --git a/README.md b/README.md index b28ba882..fe6cd283 100644 --- a/README.md +++ b/README.md @@ -86,39 +86,8 @@ enum InsuranceStatus { } ``` -# Environment Awareness - -You can configure Veil to redact or skip redacting data based on environment variables. Enable the `environment-aware` Cargo feature like so in your Cargo.toml: - -```toml -[dependencies] -veil = { version = "0.1", features = ["environment-aware"] } -``` - -## `VEIL_DISABLE_REDACTION` - -Redaction can be completely disabled by setting the `VEIL_DISABLE_REDACTION` environment variable. This is only checked once during the program lifetime for security purposes. - -## `.veil.toml` - -Redaction can also be configured on a per-project basis using a `.veil.toml` file. Put this file in your crate or workspace root and Veil will read it at compile time. - -**Please note, if you change the file, Veil won't see the changes until you do a clean build of your crate.** - -### Example - -`APP_ENV` is just an example here. You can match multiple environment variables with any UTF-8 name and value(s). - -```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] -redact = true # do redact data (default) -# OR -redact = false # don't redact data -# OR -redact = "panic" # panic at runtime -``` +# Skip redacting data +In testing environments it may be useful not to censor your logs. You can globally disable Veil's redaction behavior at runtime by enabling the *non-default* feature flag `toggle` and: +- Setting the VEIL_DISABLE_REDACTION environment variable. +or +- Calling the `disable` function. See this [example](examples/disable_redaction.rs). diff --git a/examples/disable_redaction.rs b/examples/disable_redaction.rs new file mode 100644 index 00000000..1edb2e3a --- /dev/null +++ b/examples/disable_redaction.rs @@ -0,0 +1,25 @@ +use std::env; +use veil::Redact; + +#[derive(Redact)] +pub struct Customer { + #[redact(partial)] + first_name: String, +} + +fn main() { + // If the environment variable APP_ENV is set to "dev" veil will not redact anything + if let Ok(env) = env::var("APP_ENV") { + if env == "dev" { + // Note that veil::disable needs the `toggle` feature flag enabled + veil::disable().unwrap(); + } + } + + println!( + "{:#?}", + Customer { + first_name: "John".to_string(), + } + ); +} diff --git a/src/lib.rs b/src/lib.rs index 26fa337e..030f01f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,43 +204,6 @@ //! } //! ``` //! -//! # Environment Awareness -//! -//! You can configure Veil to redact or skip redacting data based on environment variables. Enable the `environment-aware` Cargo feature like so in your Cargo.toml: -//! -//! ```toml -//! [dependencies] -//! veil = { version = "0.1", features = ["environment-aware"] } -//! ``` -//! -//! ## `VEIL_DISABLE_REDACTION` -//! -//! Redaction can be completely disabled by setting the `VEIL_DISABLE_REDACTION` environment variable. This is only checked once during the program lifetime for security purposes. -//! -//! ## `.veil.toml` -//! -//! Redaction can also be configured on a per-project basis using a `.veil.toml` file. Put this file in your crate or workspace root and Veil will read it at compile time. -//! -//! **Please note, if you change the file, Veil won't see the changes until you do a clean build of your crate.** -//! -//! ### Example -//! -//! `APP_ENV` is just an example here. You can match multiple environment variables with any UTF-8 name and value(s). -//! -//! ```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] -//! redact = true # do redact data (default) -//! ## OR -//! redact = false # don't redact data -//! ## OR -//! redact = "panic" # panic at runtime -//! ``` -//! //! # Limitations //! //! Currently, this macro only supports [`std::fmt::Debug`] formatting with no modifiers (`{:?}`) or the "alternate" modifier (`{:#?}`). @@ -255,5 +218,11 @@ pub use veil_macros::Redact; +#[cfg(feature = "toggle")] +mod toggle; +#[cfg(feature = "toggle")] +pub use toggle::*; + + #[doc(hidden)] pub mod private; diff --git a/src/private.rs b/src/private.rs index e8ff231f..2ea356de 100644 --- a/src/private.rs +++ b/src/private.rs @@ -1,5 +1,6 @@ use std::fmt::Debug; + #[repr(transparent)] pub struct DisplayDebug(String); impl std::fmt::Debug for DisplayDebug { @@ -107,15 +108,11 @@ impl RedactFlags { } } -pub fn redact( - this: &dyn Debug, - flags: RedactFlags, - #[cfg(feature = "environment-aware")] env_is_redaction_enabled: bool, -) -> DisplayDebug { +pub fn redact(this: &dyn Debug, flags: RedactFlags) -> DisplayDebug { let mut redacted = String::new(); - #[cfg(feature = "environment-aware")] - if !env_is_redaction_enabled { + #[cfg(feature = "toggle")] + if crate::toggle::get_redaction_behavior().is_plaintext() { return DisplayDebug(if flags.debug_alternate { format!("{:#?}", this) } else { @@ -165,25 +162,3 @@ pub fn redact( DisplayDebug(redacted) } - -#[cfg(feature = "environment-aware")] -pub fn env_is_redaction_enabled() -> Option { - // First check VEIL_DISABLE_REDACTION, which overrides any config file - lazy_static::lazy_static! { - // We deliberately only look this up once. - // If an attacker somehow is able to change environment variables, we don't want to give them a way of revealing sensitive data. - static ref IS_REDACTION_DISABLED: bool = std::env::var("VEIL_DISABLE_REDACTION").is_ok(); - } - if *IS_REDACTION_DISABLED { - return Some(false); - } - - // We'll run the `env_is_redaction_enabled!` macro here - // This is handled by the `fmt` module - // This is needed because we need CARGO_MANIFEST_DIR to be set by the crate being built, - // rather than this crate itself! - None -} - -#[cfg(feature = "environment-aware")] -pub use veil_macros::env_is_redaction_enabled; diff --git a/src/toggle.rs b/src/toggle.rs new file mode 100644 index 00000000..d0391ade --- /dev/null +++ b/src/toggle.rs @@ -0,0 +1,62 @@ +//! Makes it possible to disable veil's redaction behaviour +//! +use once_cell::sync::OnceCell; +/// Enum describing how Veil should behave when `std::fmt::Debug` is called on a `#[derive(Redact)]` item +#[derive(Debug, Copy, Clone)] +pub enum RedactionBehavior { + /// Redact the fields as normal + Redact, + /// Print the fields as plaintext + Plaintext, +} + +impl RedactionBehavior { + pub fn is_redact(&self) -> bool { + matches!(self, &RedactionBehavior::Redact) + } + + pub fn is_plaintext(&self) -> bool { + matches!(self, &RedactionBehavior::Plaintext) + } +} + +static DEBUG_FORMAT: OnceCell = OnceCell::new(); + +/// Sets the formatting of the debug logs +/// +/// Should only be called once, preferrably at the top of main, +/// before any calls to [`std::fmt::Debug`] or [`get_redaction_behavior`] +/// +/// If sucessfuly set the value returns Ok(()), +/// otherwise returns Err +/// ``` +/// // If the environment variable DISABLE_REDACTION is set veil will not redact anything +/// if let Ok(env) = std::env::var("APP_ENV") { +/// if env == "dev" { +/// veil::disable().unwrap(); +/// } +/// } +/// ``` +pub fn disable() -> Result<(), RedactionBehavior> { + DEBUG_FORMAT.set(RedactionBehavior::Plaintext) +} + +/// Get the current debug format value +pub(crate) fn get_redaction_behavior() -> RedactionBehavior { + if let "1" | "on" = std::env::var("VEIL_DISABLE_REDACTION").unwrap_or_default().as_str() { + *DEBUG_FORMAT.get_or_init(|| RedactionBehavior::Plaintext) + } else { + *DEBUG_FORMAT.get_or_init(|| RedactionBehavior::Redact) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn redaction_cant_be_set_after_reading() { + assert!(get_redaction_behavior().is_redact()); + disable().unwrap_err(); + assert!(get_redaction_behavior().is_redact()); + } +} diff --git a/veil-macros/Cargo.toml b/veil-macros/Cargo.toml index 6cf530ea..2d5573c8 100644 --- a/veil-macros/Cargo.toml +++ b/veil-macros/Cargo.toml @@ -3,17 +3,10 @@ name = "veil-macros" version = "0.1.0" edition = "2021" -[features] -environment-aware = ["lazy_static", "toml", "serde"] - [lib] proc-macro = true [dependencies] syn = { version = "1", features = ["full"] } quote = "1" -proc-macro2 = "1" - -lazy_static = { version = "1", optional = true } -toml = { version = "0.5", optional = true } -serde = { version = "1", features = ["derive"], optional = true } +proc-macro2 = "1" \ No newline at end of file diff --git a/veil-macros/src/enums.rs b/veil-macros/src/enums.rs index ee1909c3..d6aa52a5 100644 --- a/veil-macros/src/enums.rs +++ b/veil-macros/src/enums.rs @@ -178,21 +178,13 @@ pub(super) fn derive_redact( }); } - // Generate the `__veil_env_is_redaction_enabled` function - // Used by the `environment-aware` feature - // See the `env` module - let __veil_env_is_redaction_enabled = fmt::__veil_env_is_redaction_enabled(); - Ok(quote! { impl ::std::fmt::Debug for #name_ident { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - #__veil_env_is_redaction_enabled - let debug_alternate = f.alternate(); match self { #(Self::#variant_idents #variant_destructures => { #variant_bodies; },)* } - Ok(()) } } diff --git a/veil-macros/src/env.rs b/veil-macros/src/env.rs deleted file mode 100644 index 593883f0..00000000 --- a/veil-macros/src/env.rs +++ /dev/null @@ -1,235 +0,0 @@ -//! If the `environment-aware` feature is enabled, the user can configure Veil's behaviour in different environments. -//! -//! If the environment variable `VEIL_DISBALE_REDACTION` is set, Veil will not redact any data. -//! -//! If the user's project contains a `.veil.toml` file, Veil will use the configuration in that file to figure out whether to redact data. -//! This is done at compile time. Runtime changes to this file will have no effect. -//! -//! The configuration file allows the user to specify what environment variables and their values should enable or disable redaction. -//! -//! For example, I can configure the file to redact when APP_ENV="production", and not redact when APP_ENV="development". - -use lazy_static::lazy_static; -use proc_macro::TokenStream; -use quote::ToTokens; -use serde::Deserialize; -use std::{collections::BTreeMap, path::Path}; -use syn::spanned::Spanned; - -#[derive(Deserialize)] -#[serde(untagged)] -enum FallbackBehavior { - Redact(bool), - Panic(String), -} -impl ToTokens for FallbackBehavior { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - match self { - Self::Redact(redact) => redact.to_tokens(tokens), - Self::Panic(_) => quote! { panic!("Veil expected environment variables to be set that determine whether sensitive data should be redacted or not. Please refer to .veil.toml in the project files to see what Veil was expecting.") }.to_tokens(tokens) - } - } -} - -#[derive(Deserialize)] -/// Should we redact data based on the values of environment variables? -struct EnvRedactConfig { - #[serde(default)] - /// Redaction should be ON if the environment variable is set to one of these values. - redact: Vec, - - #[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)] -/// If none of those environment variables are present... -struct FallbackRedactConfig { - /// ...then we should [redact|not redact] the data. - redact: FallbackBehavior, -} -impl Default for FallbackRedactConfig { - fn default() -> Self { - Self { - redact: FallbackBehavior::Redact(true), - } - } -} - -#[derive(Deserialize)] -struct TomlVeilConfig { - #[serde(default)] - fallback: FallbackRedactConfig, - env: Option>, -} - -#[derive(Default)] -pub struct VeilConfig { - fallback: FallbackRedactConfig, - env: BTreeMap, -} -impl VeilConfig { - pub fn read(path: &Path) -> Result { - let config = std::fs::read_to_string(path)?; - let config: TomlVeilConfig = toml::from_str(&config)?; - - // Ensure there are no duplicate key-value environment variable pairs. - if let Some(env) = &config.env { - let mut pairs = Vec::new(); - 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); - } - } - } - } - - // Ensure the fallback is configured correctly. - if let FallbackBehavior::Panic(value) = &config.fallback.redact { - if value != "panic" { - return Err(VeilConfigError::Custom( - "fallback redaction behavior must be \"panic\"".to_string(), - )); - } - } - - Ok(Self { - env: config.env.unwrap_or_default(), - fallback: config.fallback, - }) - } -} - -#[derive(Debug)] -pub enum VeilConfigError { - IoError(std::io::Error), - TomlError(toml::de::Error), - Custom(String), -} -impl std::error::Error for VeilConfigError {} -impl std::fmt::Display for VeilConfigError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::IoError(err) => write!(f, "I/O error reading .veil.toml: {err}"), - Self::TomlError(err) => write!(f, "TOML error reading .veil.toml: {err:#?}"), - Self::Custom(err) => write!(f, ".veil.toml error: {err}"), - } - } -} -impl From for VeilConfigError { - #[inline] - fn from(err: std::io::Error) -> Self { - Self::IoError(err) - } -} -impl From for VeilConfigError { - #[inline] - fn from(err: toml::de::Error) -> Self { - Self::TomlError(err) - } -} - -lazy_static! { - static ref CACHED_VEIL_CONFIG: Result = find_config(); -} -fn find_config() -> Result { - let manifest_dir = match std::env::var_os("CARGO_MANIFEST_DIR") { - Some(manifest_dir) => manifest_dir, - None => return Ok(Default::default()), - }; - - let mut manifest_dir = Path::new(&manifest_dir); - - // Walk up the directory tree until we find a .veil.toml file - // We should stop at the workspace root - let config_path = loop { - let config_path = manifest_dir.join(".veil.toml"); - if config_path.is_file() { - break Some(config_path); - } - - // HACK! We can detect the workspace root by looking for Cargo.lock - if manifest_dir.join("Cargo.lock").is_file() { - break None; - } - - manifest_dir = match manifest_dir.parent() { - Some(parent) => parent, - None => break None, - }; - }; - - let config_path = match config_path { - Some(config_path) => config_path, - None => return Ok(Default::default()), - }; - - VeilConfig::read(&config_path) -} - -/// This macro expands to a function that returns true or false depending on whether the -/// data should be redacted based on the values of environment variables, configured by -/// `.veil.toml` in the project's files, if present. -pub fn env_is_redaction_enabled(input: TokenStream) -> TokenStream { - let config = match &*CACHED_VEIL_CONFIG { - Ok(config) => config, - Err(err) => { - return syn::Error::new(proc_macro2::TokenStream::from(input).span(), err.to_string()) - .into_compile_error() - .into() - } - }; - - 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 REDACTS: &[&str] = &[#(#redacts),*]; - if REDACTS.contains(&value.as_str()) { - return true; - } - - static SKIPS: &[&str] = &[#(#skips),*]; - if SKIPS.contains(&value.as_str()) { - return false; - } - } - } - }); - - let fallback = config.fallback.redact.to_token_stream(); - if config.env.is_empty() { - quote! { - #[inline(always)] - fn __veil_env_is_redaction_enabled() -> bool { - #fallback - } - } - .into() - } else { - quote! { - #[inline(never)] - fn __veil_env_is_redaction_enabled() -> bool { - #(#env)* - #fallback - } - } - .into() - } -} diff --git a/veil-macros/src/fmt.rs b/veil-macros/src/fmt.rs index 24d6626b..ed1e6946 100644 --- a/veil-macros/src/fmt.rs +++ b/veil-macros/src/fmt.rs @@ -115,18 +115,11 @@ impl FormatData<'_> { } } - // Generate the `__veil_env_is_redaction_enabled` function - // Used by the `environment-aware` feature - // See the `env` module - let __veil_env_is_redaction_enabled = __veil_env_is_redaction_enabled(); - Ok(match self { Self::FieldsNamed(syn::FieldsNamed { named, .. }) => { let field_names = named.iter().map(|field| field.ident.as_ref().unwrap().to_string()); quote! { - #__veil_env_is_redaction_enabled - f.debug_struct(&#name.as_ref()) #( .field(#field_names, &#field_bodies) @@ -137,8 +130,6 @@ impl FormatData<'_> { Self::FieldsUnnamed(syn::FieldsUnnamed { .. }) => { quote! { - #__veil_env_is_redaction_enabled - f.debug_tuple(&#name.as_ref()) #( .field(&#field_bodies) @@ -161,34 +152,14 @@ pub(crate) fn generate_redact_call( // This is the one place where we actually track whether the derive macro had any effect! Nice. unused.redacted_something(); - // Environment awareness (we assume that we injected the `__veil_env_is_redaction_enabled` function earlier) - let env_is_redaction_enabled = if cfg!(feature = "environment-aware") { - quote! { - , ::veil::private::env_is_redaction_enabled().unwrap_or_else(__veil_env_is_redaction_enabled) - } - } else { - proc_macro2::TokenStream::default() - }; - quote! { ::veil::private::redact(#field_accessor, ::veil::private::RedactFlags { debug_alternate, is_option: #is_option, #field_flags - } #env_is_redaction_enabled) + }) } } else { field_accessor } } - -/// Generates the `__veil_env_is_redaction_enabled` function -pub fn __veil_env_is_redaction_enabled() -> proc_macro2::TokenStream { - if cfg!(feature = "environment-aware") { - // Generate a function that returns whether redaction is enabled based on the environment. - // The compiler will be able to deduplicate the function, so we won't be generating hundreds of copies of it in the final binary. - quote! { ::veil::private::env_is_redaction_enabled!(); } - } else { - proc_macro2::TokenStream::default() - } -} diff --git a/veil-macros/src/lib.rs b/veil-macros/src/lib.rs index bcc685f8..0d43f334 100644 --- a/veil-macros/src/lib.rs +++ b/veil-macros/src/lib.rs @@ -73,12 +73,3 @@ pub fn derive_redact(item: TokenStream) -> TokenStream { Err(err) => err.into_compile_error().into(), } } - -#[cfg(feature = "environment-aware")] -mod env; - -#[cfg(feature = "environment-aware")] -#[proc_macro] -pub fn env_is_redaction_enabled(input: TokenStream) -> TokenStream { - env::env_is_redaction_enabled(input) -} diff --git a/veil-tests/environment-aware-disable/Cargo.toml b/veil-tests/disable-redaction-test/Cargo.toml similarity index 50% rename from veil-tests/environment-aware-disable/Cargo.toml rename to veil-tests/disable-redaction-test/Cargo.toml index 4d1c9411..8cea0630 100644 --- a/veil-tests/environment-aware-disable/Cargo.toml +++ b/veil-tests/disable-redaction-test/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "veil-tests-environment-aware-disable" +name = "veil-tests-disable-redaction" version = "0.1.0" edition = "2021" publish = false [dependencies] -veil = { path = "../../", features = ["environment-aware"] } +veil = { path = "../../", features = ["toggle"] } veil-tests = { path = "../" } diff --git a/veil-tests/disable-redaction-test/src/lib.rs b/veil-tests/disable-redaction-test/src/lib.rs new file mode 100644 index 00000000..41283a77 --- /dev/null +++ b/veil-tests/disable-redaction-test/src/lib.rs @@ -0,0 +1,12 @@ +#![cfg_attr(not(test), allow(unused))] +//! Simple test that ensures veil can actually be disabled + +use veil::Redact; +use veil_tests::{SENSITIVE_DATA, assert_has_sensitive_data}; +#[test] +fn test_veil_can_be_disabled() { + #[derive(Redact)] + struct SensitiveWrapper(#[redact] String); + veil::disable().ok(); + assert_has_sensitive_data(SensitiveWrapper(SENSITIVE_DATA[0].to_string())); +} diff --git a/veil-tests/environment-aware-disable/src/lib.rs b/veil-tests/environment-aware-disable/src/lib.rs deleted file mode 100644 index db7096b4..00000000 --- a/veil-tests/environment-aware-disable/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! This needs its own crate because VEIL_DISABLE_REDACTION is only checked once -//! and another test in the other crates might run first, causing THIS test to fail. - -#![cfg_attr(not(test), allow(unused))] - -use veil::Redact; -use veil_tests::{assert_has_sensitive_data, SENSITIVE_DATA}; - -#[derive(Redact)] -#[redact(all, partial)] -struct Redactable(&'static str); - -#[test] -fn test_disable_redact_env_var() { - std::env::set_var("VEIL_DISABLE_REDACTION", "1"); - assert_has_sensitive_data(Redactable(SENSITIVE_DATA[1])); - - // Ensure it's only checked once - std::env::remove_var("VEIL_DISABLE_REDACTION"); - assert_has_sensitive_data(Redactable(SENSITIVE_DATA[1])); -} diff --git a/veil-tests/environment-aware-fallback-off/.veil.toml b/veil-tests/environment-aware-fallback-off/.veil.toml deleted file mode 100644 index d87ecc8c..00000000 --- a/veil-tests/environment-aware-fallback-off/.veil.toml +++ /dev/null @@ -1,2 +0,0 @@ -[fallback] -redact = false diff --git a/veil-tests/environment-aware-fallback-off/Cargo.toml b/veil-tests/environment-aware-fallback-off/Cargo.toml deleted file mode 100644 index ef56b5e8..00000000 --- a/veil-tests/environment-aware-fallback-off/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "veil-tests-environment-aware-fallback-off" -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -veil = { path = "../../", features = ["environment-aware"] } -veil-tests = { path = "../" } diff --git a/veil-tests/environment-aware-fallback-off/src/lib.rs b/veil-tests/environment-aware-fallback-off/src/lib.rs deleted file mode 100644 index b5ca97c8..00000000 --- a/veil-tests/environment-aware-fallback-off/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![cfg_attr(not(test), allow(unused))] - -use veil::Redact; -use veil_tests::{assert_has_sensitive_data, SENSITIVE_DATA}; - -#[derive(Redact)] -#[redact(all, partial)] -struct Redactable(&'static str); - -#[test] -fn test_fallback_redact_off() { - assert_has_sensitive_data(Redactable(SENSITIVE_DATA[1])); -} diff --git a/veil-tests/environment-aware-fallback-on/.veil.toml b/veil-tests/environment-aware-fallback-on/.veil.toml deleted file mode 100644 index 39ad76ac..00000000 --- a/veil-tests/environment-aware-fallback-on/.veil.toml +++ /dev/null @@ -1,2 +0,0 @@ -[fallback] -redact = true diff --git a/veil-tests/environment-aware-fallback-on/Cargo.toml b/veil-tests/environment-aware-fallback-on/Cargo.toml deleted file mode 100644 index d4bd71fc..00000000 --- a/veil-tests/environment-aware-fallback-on/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "veil-tests-environment-aware-fallback-on" -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -veil = { path = "../../", features = ["environment-aware"] } -veil-tests = { path = "../" } diff --git a/veil-tests/environment-aware-fallback-on/src/lib.rs b/veil-tests/environment-aware-fallback-on/src/lib.rs deleted file mode 100644 index 21b68719..00000000 --- a/veil-tests/environment-aware-fallback-on/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![cfg_attr(not(test), allow(unused))] - -use veil::Redact; -use veil_tests::{assert_no_sensitive_data, SENSITIVE_DATA}; - -#[derive(Redact)] -#[redact(all, partial)] -struct Redactable(&'static str); - -#[test] -fn test_fallback_redact_on() { - assert_no_sensitive_data(Redactable(SENSITIVE_DATA[1])); -} diff --git a/veil-tests/environment-aware-fallback-panic/.veil.toml b/veil-tests/environment-aware-fallback-panic/.veil.toml deleted file mode 100644 index 371629c0..00000000 --- a/veil-tests/environment-aware-fallback-panic/.veil.toml +++ /dev/null @@ -1,2 +0,0 @@ -[fallback] -redact = "panic" diff --git a/veil-tests/environment-aware-fallback-panic/Cargo.toml b/veil-tests/environment-aware-fallback-panic/Cargo.toml deleted file mode 100644 index 1371ba0b..00000000 --- a/veil-tests/environment-aware-fallback-panic/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "veil-tests-environment-aware-fallback-panic" -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -veil = { path = "../../", features = ["environment-aware"] } diff --git a/veil-tests/environment-aware-fallback-panic/src/lib.rs b/veil-tests/environment-aware-fallback-panic/src/lib.rs deleted file mode 100644 index cbd849f4..00000000 --- a/veil-tests/environment-aware-fallback-panic/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![cfg_attr(not(test), allow(unused))] - -use veil::Redact; - -#[derive(Redact)] -#[redact(all, partial)] -struct Redactable(&'static str); - -#[test] -#[should_panic] -fn test_fallback_redact_on() { - format!("{:?}", Redactable("")); -} diff --git a/veil-tests/environment-aware/.veil.toml b/veil-tests/environment-aware/.veil.toml deleted file mode 100644 index 352e18e5..00000000 --- a/veil-tests/environment-aware/.veil.toml +++ /dev/null @@ -1,3 +0,0 @@ -[env.APP_ENV] -skip-redact = ["dev", "qa"] -redact = ["production", "staging"] diff --git a/veil-tests/environment-aware/Cargo.toml b/veil-tests/environment-aware/Cargo.toml deleted file mode 100644 index f4b8e3b5..00000000 --- a/veil-tests/environment-aware/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "veil-tests-environment-aware" -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -parking_lot = "0.12" # needed for non-poisoning mutex -veil = { path = "../../", features = ["environment-aware"] } -veil-tests = { path = "../" } diff --git a/veil-tests/environment-aware/src/lib.rs b/veil-tests/environment-aware/src/lib.rs deleted file mode 100644 index 58483204..00000000 --- a/veil-tests/environment-aware/src/lib.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![cfg_attr(not(test), allow(unused))] - -use parking_lot::Mutex; -use veil::Redact; -use veil_tests::{assert_has_sensitive_data, assert_no_sensitive_data}; - -const SENSITIVE_DATA: &str = veil_tests::SENSITIVE_DATA[1]; - -static ENVIRONMENT_LOCK: Mutex<()> = parking_lot::const_mutex(()); - -#[derive(Redact)] -#[redact(all, partial)] -struct Redactable(&'static str); - -#[test] -fn test_production() { - // We've set up redaction to happen when APP_ENV="production" - let _lock = ENVIRONMENT_LOCK.lock(); - - std::env::set_var("APP_ENV", "production"); - - assert_no_sensitive_data(Redactable(SENSITIVE_DATA)); -} - -#[test] -fn test_staging() { - // We've set up redaction to happen when APP_ENV="staging" - let _lock = ENVIRONMENT_LOCK.lock(); - - std::env::set_var("APP_ENV", "staging"); - - assert_no_sensitive_data(Redactable(SENSITIVE_DATA)); -} - -#[test] -fn test_dev() { - // We've set up redaction to NOT happen when APP_ENV="dev" - let _lock = ENVIRONMENT_LOCK.lock(); - - std::env::set_var("APP_ENV", "dev"); - - assert_has_sensitive_data(Redactable(SENSITIVE_DATA)); -} - -#[test] -fn test_qa() { - // We've set up redaction to NOT happen when APP_ENV="qa" - let _lock = ENVIRONMENT_LOCK.lock(); - - std::env::set_var("APP_ENV", "qa"); - - assert_has_sensitive_data(Redactable(SENSITIVE_DATA)); -}