diff --git a/Cargo.lock b/Cargo.lock index 14f2c27d..689c9189 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,6 +129,37 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "assert_cmd" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd389a4b2970a01282ee455294913c0a43724daedcd1a24c3eb0ec1c1320b66" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "assert_fs" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652f6cb1f516886fcfee5e7a5c078b9ade62cfcb889524efe5a64d682dd27a9" +dependencies = [ + "anstyle", + "doc-comment", + "globwalk", + "predicates", + "predicates-core", + "predicates-tree", + "tempfile", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -207,6 +238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", + "regex-automata", "serde", ] @@ -427,6 +459,8 @@ name = "cotp" version = "1.9.7" dependencies = [ "aes-gcm", + "assert_cmd", + "assert_fs", "base64", "chacha20poly1305", "clap", @@ -442,6 +476,7 @@ dependencies = [ "hex", "hmac", "md-5", + "predicates", "qrcode", "ratatui", "rpassword", @@ -451,6 +486,7 @@ dependencies = [ "serde_json", "sha1", "sha2", + "test-case", "url", "urlencoding", "zeroize", @@ -465,6 +501,25 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -636,6 +691,12 @@ dependencies = [ "syn", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.10.7" @@ -688,6 +749,12 @@ dependencies = [ "libloading", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "document-features" version = "0.2.11" @@ -747,6 +814,21 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -840,6 +922,17 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "globwalk" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +dependencies = [ + "bitflags 2.9.1", + "ignore", + "walkdir", +] + [[package]] name = "hashbrown" version = "0.15.3" @@ -994,6 +1087,22 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "image" version = "0.25.6" @@ -1223,6 +1332,12 @@ dependencies = [ "memoffset", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "num-conv" version = "0.1.0" @@ -1415,6 +1530,36 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -1499,6 +1644,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + [[package]] name = "regex-automata" version = "0.4.9" @@ -1602,6 +1759,15 @@ dependencies = [ "cipher", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1821,6 +1987,58 @@ dependencies = [ "syn", ] +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix 1.0.7", + "windows-sys 0.59.0", +] + +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "test-case" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" +dependencies = [ + "test-case-macros", +] + +[[package]] +name = "test-case-core" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "test-case-macros" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "test-case-core", +] + [[package]] name = "thiserror" version = "2.0.12" @@ -2016,6 +2234,25 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2132,6 +2369,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "winapi-wsapoll" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index 4d5a346a..650a4e0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,3 +55,9 @@ color-eyre = "0.6.5" enum_dispatch = "0.3.13" derive_builder = "0.20.1" globset = "0.4.16" + +[dev-dependencies] +assert_cmd = "2.0.17" +assert_fs = "1.1.3" +predicates = "3.1.3" +test-case = "3.3.1" diff --git a/src/arguments/add.rs b/src/arguments/add.rs index 875de4c8..030978b3 100644 --- a/src/arguments/add.rs +++ b/src/arguments/add.rs @@ -1,5 +1,7 @@ +use std::io::{self, BufRead}; + use clap::{Args, value_parser}; -use color_eyre::eyre::{ErrReport, Result}; +use color_eyre::eyre::{self, ErrReport, Result}; use zeroize::Zeroize; @@ -60,6 +62,10 @@ pub struct AddArgs { required_if_eq("otp_type", "MOTP") )] pub pin: Option, + + /// Pass the secret through the standard input + #[arg(long = "secret-stdin", default_value_t = false)] + take_secret_from_stdin: bool, } impl SubcommandExecutor for AddArgs { @@ -79,7 +85,15 @@ impl SubcommandExecutor for AddArgs { } fn get_from_args(matches: AddArgs) -> color_eyre::Result { - let secret = rpassword::prompt_password("Insert the secret: ").map_err(ErrReport::from)?; + let secret = if matches.take_secret_from_stdin { + if let Some(password) = io::stdin().lock().lines().next() { + password.map_err(ErrReport::from) + } else { + Err(eyre::eyre!("Error during reading from stdin")) + } + } else { + rpassword::prompt_password("Insert the secret: ").map_err(ErrReport::from) + }?; map_args_to_code(secret, matches) } diff --git a/test_samples/cli_integration_test/empty_database b/test_samples/cli_integration_test/empty_database new file mode 100644 index 00000000..56c86801 --- /dev/null +++ b/test_samples/cli_integration_test/empty_database @@ -0,0 +1 @@ +{"version":1,"nonce":"MRFGJOdHjt9wrSbqlWHrwmMGENmjjCMh","salt":"eQkRyDUTaaWNVy/+S/CXIw==","cipher":"tBtvz2AOp7XPUJGSLkm+Ss+exKFjd0eA1UWd58nM/I45P7VmdK6/28c9vw=="} \ No newline at end of file diff --git a/tests/add_integration_tests.rs b/tests/add_integration_tests.rs new file mode 100644 index 00000000..8945d31c --- /dev/null +++ b/tests/add_integration_tests.rs @@ -0,0 +1,56 @@ +#[cfg(not(target_os = "windows"))] // TODO, Integration tests currently does not work on Windows +mod add_integration_tests { + use assert_cmd::Command; + use predicates::{ord::eq, str::is_empty}; + use test_case::test_case; + + #[test] + fn add_without_label_should_fail() { + // Arrange / Act + let assertion = Command::cargo_bin("cotp") + .unwrap() + .arg("--database-path") + .arg("test_samples/cli_integration_test/empty_database") + .arg("add") + .assert(); + + // Assert + assertion.failure().code(2).stdout(is_empty()).stderr(eq( + "error: the following required arguments were not provided: + --otpuri + --label