From 43e7fb8b4280e2b28964089720bbc99f670a1d05 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 28 Aug 2024 13:22:36 +0000 Subject: [PATCH] rage: Allow piping input when encrypting with passphrase on Unix Closes str4d/rage#374. --- rage/CHANGELOG.md | 4 ++++ rage/src/bin/rage/error.rs | 7 ++++++- rage/src/bin/rage/main.rs | 10 ++++++++-- .../rage/encrypt-passphrase-without-file-argument.toml | 7 ++++++- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/rage/CHANGELOG.md b/rage/CHANGELOG.md index e49f64f9..50a288c4 100644 --- a/rage/CHANGELOG.md +++ b/rage/CHANGELOG.md @@ -12,6 +12,10 @@ to 1.0.0 are beta releases. ### Added - Partial French translation! +### Fixed +- [Unix] Files can now be encrypted with `rage --passphrase` when piped over + stdin, without requiring an explicit `-` argument as `INPUT`. + ## [0.10.0] - 2024-02-04 ### Added - Russian translation! diff --git a/rage/src/bin/rage/error.rs b/rage/src/bin/rage/error.rs index 3cb4a3ef..596e52b3 100644 --- a/rage/src/bin/rage/error.rs +++ b/rage/src/bin/rage/error.rs @@ -23,7 +23,10 @@ macro_rules! wlnfl { pub(crate) enum EncryptError { Age(age::EncryptError), - BrokenPipe { is_stdout: bool, source: io::Error }, + BrokenPipe { + is_stdout: bool, + source: io::Error, + }, IdentityRead(age::cli_common::ReadError), Io(io::Error), MissingRecipients, @@ -31,6 +34,7 @@ pub(crate) enum EncryptError { MixedRecipientAndPassphrase, MixedRecipientsFileAndPassphrase, PassphraseTimedOut, + #[cfg(not(unix))] PassphraseWithoutFileArgument, PluginNameFlag, } @@ -84,6 +88,7 @@ impl fmt::Display for EncryptError { wfl!(f, "err-enc-mixed-recipients-file-passphrase") } EncryptError::PassphraseTimedOut => wfl!(f, "err-passphrase-timed-out"), + #[cfg(not(unix))] EncryptError::PassphraseWithoutFileArgument => { wfl!(f, "err-enc-passphrase-without-file") } diff --git a/rage/src/bin/rage/main.rs b/rage/src/bin/rage/main.rs index a75a3972..545e2513 100644 --- a/rage/src/bin/rage/main.rs +++ b/rage/src/bin/rage/main.rs @@ -108,6 +108,7 @@ fn encrypt(opts: AgeOptions) -> Result<(), error::EncryptError> { (Format::Binary, file_io::OutputFormat::Binary) }; + #[cfg(not(unix))] let has_file_argument = opts.input.is_some(); let (input, output) = set_up_io(opts.input, opts.output, output_format)?; @@ -134,8 +135,13 @@ fn encrypt(opts: AgeOptions) -> Result<(), error::EncryptError> { return Err(error::EncryptError::MixedRecipientsFileAndPassphrase); } - if !has_file_argument { - return Err(error::EncryptError::PassphraseWithoutFileArgument); + // The `rpassword` crate opens `/dev/tty` directly on Unix, so we don't have + // any conflict with stdin. + #[cfg(not(unix))] + { + if !has_file_argument { + return Err(error::EncryptError::PassphraseWithoutFileArgument); + } } match read_or_generate_passphrase() { diff --git a/rage/tests/cmd/rage/encrypt-passphrase-without-file-argument.toml b/rage/tests/cmd/rage/encrypt-passphrase-without-file-argument.toml index 2f393d40..4b0c6bee 100644 --- a/rage/tests/cmd/rage/encrypt-passphrase-without-file-argument.toml +++ b/rage/tests/cmd/rage/encrypt-passphrase-without-file-argument.toml @@ -4,8 +4,13 @@ status = "failed" stdin = "" stdout = "" stderr = """ -Error: File to encrypt must be passed as an argument when using -p/--passphrase. +Error: Parsing Error: Error { input: "", code: Tag } [ Did rage not do what you expected? Could an error be more useful? ] [ Tell us: https://str4d.xyz/rage/report ] """ + +# We get an error from the `pinentry` crate because we've passed a real but invalid binary +# that does not speak the pinentry protocol. +[env.add] +PINENTRY_PROGRAM = "true"