diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 0dc1160..3e27e40 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use clap::{Args, Parser, Subcommand, ValueEnum, ValueHint}; +use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum, ValueHint}; use regex::Regex; use rops::*; @@ -29,6 +29,9 @@ pub struct EncryptArgs { pub aws_kms_keys: Vec<::KeyId>, #[command(flatten)] pub partial_encryption_args: Option, + /// Requires a partial encryption setting + #[arg(long, display_order = 11, requires = "partial_encryption", action(ArgAction::SetTrue))] + pub mac_only_encrypted: Option, #[command(flatten)] pub input_args: InputArgs, } @@ -44,14 +47,18 @@ pub struct InputArgs { } #[derive(Args)] -#[group(multiple = false)] +#[group(id = "partial_encryption", multiple = false)] pub struct PartialEncryptionArgs { + /// Encrypt values matching key suffix. #[arg(long, display_order = 10, value_name = "STRING")] encrypted_suffix: Option, + /// Encrypt values matching key regex. #[arg(long, display_order = 10, value_name = "REGEX")] encrypted_regex: Option, + /// Skip encrypting values matching key suffix. #[arg(long, display_order = 10, value_name = "STRING")] unencrypted_suffix: Option, + /// Skip encrypting values matching key regex. #[arg(long, display_order = 10, value_name = "REGEX")] unencrypted_regex: Option, } diff --git a/crates/cli/src/run.rs b/crates/cli/src/run.rs index 0f2ab88..2f71902 100644 --- a/crates/cli/src/run.rs +++ b/crates/cli/src/run.rs @@ -35,6 +35,10 @@ pub fn run() -> anyhow::Result<()> { rops_file_builder = rops_file_builder.with_partial_encryption(partial_encryption_args.into()) } + if encrypt_args.mac_only_encrypted.unwrap_or_default() { + rops_file_builder = rops_file_builder.mac_only_encrypted() + } + let encrypted_rops_file = rops_file_builder.encrypt::()?; println!("{}", encrypted_rops_file); diff --git a/crates/cli/tests/mod.rs b/crates/cli/tests/mod.rs index c75dd6e..a9b861c 100644 --- a/crates/cli/tests/mod.rs +++ b/crates/cli/tests/mod.rs @@ -43,17 +43,22 @@ fn encrypts_from_file() { #[test] fn encrypts_with_partial_encryption() { - let mut cmd = Command::package_command().encrypt(); - cmd.arg(format!("--unencrypted-suffix={}", PartialEncryptionConfig::mock_display())); - let plaintext = sops_yaml_str!("age_unencrypted_suffix_plaintext"); - let output = cmd.run_piped(plaintext); - output.assert_success(); - + let output = Command::package_command().encrypt().partial_encryption().run_piped(plaintext); let decrypted_file = decrypt_output::(output); assert_eq!(Some(PartialEncryptionConfig::mock()), decrypted_file.metadata().partial_encryption) } +#[test] +fn encrypts_with_mac_encrypted_only() { + let mut cmd = Command::package_command().encrypt().partial_encryption(); + cmd.arg("--mac-only-encrypted"); + + let plaintext = sops_yaml_str!("age_mac_only_encrypted_plaintext"); + let decrypted_file = decrypt_output::(cmd.run_piped(plaintext)); + assert_eq!(Some(true), decrypted_file.metadata().mac_only_encrypted) +} + #[test] fn decrypts_from_stdin() { let encrypted_str = sops_yaml_str!("age_example"); @@ -68,24 +73,34 @@ fn decrypts_from_file() { assert_decrypted_output(cmd.run_tty()) } -#[rustfmt::skip] -trait EncryptCommand { fn encrypt(self) -> Self; } +trait EncryptCommand { + fn encrypt(self) -> Self; + + fn partial_encryption(self) -> Self; +} impl EncryptCommand for Command { fn encrypt(mut self) -> Self { self.arg("encrypt"); self.args(["--age", &::KeyId::mock_display()]); self.common_args() } + + fn partial_encryption(mut self) -> Self { + self.arg(format!("--unencrypted-suffix={}", PartialEncryptionConfig::mock_display())); + self + } } fn assert_encrypted(encrypted_output: Output, plaintext: &str) { - encrypted_output.assert_success(); let decrypted_file = decrypt_output::(encrypted_output); pretty_assertions::assert_eq!(plaintext, decrypted_file.map().to_string()); } fn decrypt_output(encrypted_output: Output) -> RopsFile, YamlFileFormat> { + encrypted_output.assert_success(); + I::set_mock_private_key_env_var(); + encrypted_output .stdout_str() .parse::, YamlFileFormat>>()