From d6c4411e97a1fedce07f2fc1698a953c0f1fa56d Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Sun, 3 Nov 2024 15:27:54 +0100 Subject: [PATCH 1/6] add support to enpass --- lib/kamal/secrets/adapters/enpass.rb | 59 +++++++++++++++++ test/secrets/enpass_adapter_test.rb | 99 ++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 lib/kamal/secrets/adapters/enpass.rb create mode 100644 test/secrets/enpass_adapter_test.rb diff --git a/lib/kamal/secrets/adapters/enpass.rb b/lib/kamal/secrets/adapters/enpass.rb new file mode 100644 index 000000000..b698d8c6d --- /dev/null +++ b/lib/kamal/secrets/adapters/enpass.rb @@ -0,0 +1,59 @@ +require "open3" + +class Kamal::Secrets::Adapters::Enpass < Kamal::Secrets::Adapters::Base + private + def login(account) + # There is no concept of session in enpass-cli + true + end + + def fetch_secrets(secrets, account:, session:) + secrets_titles = fetch_secret_titles(secrets) + + # Enpass outputs result as stderr, I did not find a way to stub backticks and output to stderr. Open3 did the job. + _stdout, stderr, status = Open3.capture3("enpass-cli -vault #{account.shellescape} show #{secrets_titles.map(&:shellescape).join(" ")}") + raise RuntimeError, "Could not read #{secrets} from Enpass" unless status.success? + + parse_result_and_take_secrets(stderr, secrets) + end + + def check_dependencies! + raise RuntimeError, "Enpass CLI is not installed" unless cli_installed? + end + + def cli_installed? + `enpass-cli version 2> /dev/null` + $?.success? + end + + def fetch_secret_titles(secrets) + secrets.reduce(Set.new) do |acc, secret| + # Use rpartition to split the string at the last '/' + key, separator, value = secret.rpartition("/") + if key.empty? + acc << value + else + acc << key + end + end.to_a + end + + def parse_result_and_take_secrets(unparsed_result, secrets) + unparsed_result.split("\n").reduce({}) do |acc, line| + title = line[/title:\s*(\w+)/, 1] + label = line[/label:\s*(.*?)\s{2}/, 1] + password = line[/password:\s*([^"]+)/, 1] + + # If title and label are not empty and password is defined, add to the hash + if title && !password.to_s.empty? + key = label.nil? || label.empty? ? title : "#{title}/#{label}" + if secrets.include?(title) || secrets.include?(key) + raise RuntimeError, "#{key} is present more than once" if acc[key] + acc[key] = password + end + end + + acc + end + end +end diff --git a/test/secrets/enpass_adapter_test.rb b/test/secrets/enpass_adapter_test.rb new file mode 100644 index 000000000..d64bb6f6c --- /dev/null +++ b/test/secrets/enpass_adapter_test.rb @@ -0,0 +1,99 @@ +require "test_helper" + +class EnpassAdapterTest < SecretAdapterTestCase + setup do + `true` # Ensure $? is 0 + end + + test "fetch without CLI installed" do + stub_ticks_with("enpass-cli version 2> /dev/null", succeed: false) + + error = assert_raises RuntimeError do + JSON.parse(shellunescape(run_command("fetch", "mynote"))) + end + + assert_equal "Enpass CLI is not installed", error.message + end + + test "fetch one item" do + stub_ticks_with("enpass-cli version 2> /dev/null") + + stderr_response = <<~RESULT + time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_1 password: my-password-1 + RESULT + + Open3.stubs(:capture3).returns([ "", stderr_response, OpenStruct.new(success?: true) ]) + + json = JSON.parse(shellunescape(run_command("fetch", "FooBar/SECRET_1"))) + + expected_json = { "FooBar/SECRET_1" => "my-password-1" } + + assert_equal expected_json, json + end + + test "fetch multiple items" do + stub_ticks_with("enpass-cli version 2> /dev/null") + + stderr_response = <<~RESULT + time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_1 password: my-password-1 + time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_2 password: my-password-2 + time="2024-11-03T13:34:39+01:00" level=info msg="> title: Hello login: cat.: computer label: SECRET_3 password: my-password-3 + RESULT + + Open3.stubs(:capture3).returns([ "", stderr_response, OpenStruct.new(success?: true) ]) + + json = JSON.parse(shellunescape(run_command("fetch", "FooBar/SECRET_1", "FooBar/SECRET_2"))) + + expected_json = { "FooBar/SECRET_1" => "my-password-1", "FooBar/SECRET_2" => "my-password-2" } + + assert_equal expected_json, json + end + + test "fetch multiple items with from" do + stub_ticks_with("enpass-cli version 2> /dev/null") + + stderr_response = <<~RESULT + time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_1 password: my-password-1 + time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_2 password: my-password-2 + time="2024-11-03T13:34:39+01:00" level=info msg="> title: Hello login: cat.: computer label: SECRET_3 password: my-password-3 + RESULT + + Open3.stubs(:capture3).returns([ "", stderr_response, OpenStruct.new(success?: true) ]) + + json = JSON.parse(shellunescape(run_command("fetch", "--from", "FooBar", "SECRET_1", "SECRET_2"))) + + expected_json = { "FooBar/SECRET_1" => "my-password-1", "FooBar/SECRET_2" => "my-password-2" } + + assert_equal expected_json, json + end + + test "fetch all with from" do + stub_ticks_with("enpass-cli version 2> /dev/null") + + stderr_response = <<~RESULT + time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_1 password: my-password-1 + time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_2 password: my-password-2 + time="2024-11-03T13:34:39+01:00" level=info msg="> title: Hello login: cat.: computer label: SECRET_3 password: my-password-3 + time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: password: my-password-3 + RESULT + + Open3.stubs(:capture3).returns([ "", stderr_response, OpenStruct.new(success?: true) ]) + + json = JSON.parse(shellunescape(run_command("fetch", "FooBar"))) + + expected_json = { "FooBar/SECRET_1" => "my-password-1", "FooBar/SECRET_2" => "my-password-2", "FooBar" => "my-password-3" } + + assert_equal expected_json, json + end + + private + def run_command(*command) + stdouted do + Kamal::Cli::Secrets.start \ + [ *command, + "-c", "test/fixtures/deploy_with_accessories.yml", + "--adapter", "enpass", + "--account", "vault-path" ] + end + end +end From 4d09f3c242ed7b1fb400044ecbec2f685b4cdf25 Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Sun, 3 Nov 2024 15:34:59 +0100 Subject: [PATCH 2/6] add more docs --- lib/kamal/secrets/adapters/enpass.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/kamal/secrets/adapters/enpass.rb b/lib/kamal/secrets/adapters/enpass.rb index b698d8c6d..ae2575452 100644 --- a/lib/kamal/secrets/adapters/enpass.rb +++ b/lib/kamal/secrets/adapters/enpass.rb @@ -1,5 +1,9 @@ require "open3" +## +# Enpass is different from most password managers, in a way that it's offline. A path to a vault is treated as account. +# +# Pass it like so: `kamal secrets fetch --adapter enpass --account /Users/YOUR_USERNAME/Library/Containers/in.sinew.Enpass-Desktop/Data/Documents/Vaults/primary --from MY_PROD_SERVER` class Kamal::Secrets::Adapters::Enpass < Kamal::Secrets::Adapters::Base private def login(account) @@ -28,7 +32,7 @@ def cli_installed? def fetch_secret_titles(secrets) secrets.reduce(Set.new) do |acc, secret| - # Use rpartition to split the string at the last '/' + # Sometimes secrets contain a '/', sometimes not key, separator, value = secret.rpartition("/") if key.empty? acc << value @@ -44,7 +48,6 @@ def parse_result_and_take_secrets(unparsed_result, secrets) label = line[/label:\s*(.*?)\s{2}/, 1] password = line[/password:\s*([^"]+)/, 1] - # If title and label are not empty and password is defined, add to the hash if title && !password.to_s.empty? key = label.nil? || label.empty? ? title : "#{title}/#{label}" if secrets.include?(title) || secrets.include?(key) From b356b08069a312c1a0fec1a9999bcd9c438b61f4 Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Sun, 3 Nov 2024 15:50:11 +0100 Subject: [PATCH 3/6] improve password parsing --- lib/kamal/secrets/adapters/enpass.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/kamal/secrets/adapters/enpass.rb b/lib/kamal/secrets/adapters/enpass.rb index ae2575452..616360323 100644 --- a/lib/kamal/secrets/adapters/enpass.rb +++ b/lib/kamal/secrets/adapters/enpass.rb @@ -44,9 +44,9 @@ def fetch_secret_titles(secrets) def parse_result_and_take_secrets(unparsed_result, secrets) unparsed_result.split("\n").reduce({}) do |acc, line| - title = line[/title:\s*(\w+)/, 1] - label = line[/label:\s*(.*?)\s{2}/, 1] - password = line[/password:\s*([^"]+)/, 1] + title = line[/title:\s{1}(\w+)/, 1] + label = line[/label:\s{1}(.*?)\s{2}/, 1] + password = line[/password:\s{1}([^"]+)/, 1] if title && !password.to_s.empty? key = label.nil? || label.empty? ? title : "#{title}/#{label}" From 8d7a6403b5b2355e57e6f94f915f9f508043c6f1 Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Thu, 7 Nov 2024 17:44:28 +0100 Subject: [PATCH 4/6] enpass-cli now has JSON support --- lib/kamal/secrets/adapters/enpass.rb | 35 ++++++++-------- test/secrets/enpass_adapter_test.rb | 60 +++++++++++++++------------- 2 files changed, 52 insertions(+), 43 deletions(-) diff --git a/lib/kamal/secrets/adapters/enpass.rb b/lib/kamal/secrets/adapters/enpass.rb index 616360323..773d1418d 100644 --- a/lib/kamal/secrets/adapters/enpass.rb +++ b/lib/kamal/secrets/adapters/enpass.rb @@ -15,10 +15,9 @@ def fetch_secrets(secrets, account:, session:) secrets_titles = fetch_secret_titles(secrets) # Enpass outputs result as stderr, I did not find a way to stub backticks and output to stderr. Open3 did the job. - _stdout, stderr, status = Open3.capture3("enpass-cli -vault #{account.shellescape} show #{secrets_titles.map(&:shellescape).join(" ")}") - raise RuntimeError, "Could not read #{secrets} from Enpass" unless status.success? + result = `enpass-cli -json -vault #{account.shellescape} show #{secrets.map(&:shellescape).join(" ")}`.strip - parse_result_and_take_secrets(stderr, secrets) + parse_result_and_take_secrets(result, secrets) end def check_dependencies! @@ -31,32 +30,36 @@ def cli_installed? end def fetch_secret_titles(secrets) - secrets.reduce(Set.new) do |acc, secret| - # Sometimes secrets contain a '/', sometimes not + secrets.reduce(Set.new) do |secret_titles, secret| + # Sometimes secrets contain a '/', when the intent is to fetch a single password for an item. Example: FooBar/DB_PASSWORD + # Another case is, when the intent is to fetch all passwords for an item. Example: FooBar (and FooBar may have multiple different passwords) key, separator, value = secret.rpartition("/") if key.empty? - acc << value + secret_titles << value else - acc << key + secret_titles << key end end.to_a end def parse_result_and_take_secrets(unparsed_result, secrets) - unparsed_result.split("\n").reduce({}) do |acc, line| - title = line[/title:\s{1}(\w+)/, 1] - label = line[/label:\s{1}(.*?)\s{2}/, 1] - password = line[/password:\s{1}([^"]+)/, 1] + result = JSON.parse(unparsed_result) + + result.reduce({}) do |secrets_with_passwords, item| + title = item["title"] + label = item["label"] + password = item["password"] + + if title && password.present? + key = [ title, label ].compact.reject(&:empty?).join("/") - if title && !password.to_s.empty? - key = label.nil? || label.empty? ? title : "#{title}/#{label}" if secrets.include?(title) || secrets.include?(key) - raise RuntimeError, "#{key} is present more than once" if acc[key] - acc[key] = password + raise RuntimeError, "#{key} is present more than once" if secrets_with_passwords[key] + secrets_with_passwords[key] = password end end - acc + secrets_with_passwords end end end diff --git a/test/secrets/enpass_adapter_test.rb b/test/secrets/enpass_adapter_test.rb index d64bb6f6c..852c7c9bf 100644 --- a/test/secrets/enpass_adapter_test.rb +++ b/test/secrets/enpass_adapter_test.rb @@ -18,11 +18,11 @@ class EnpassAdapterTest < SecretAdapterTestCase test "fetch one item" do stub_ticks_with("enpass-cli version 2> /dev/null") - stderr_response = <<~RESULT - time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_1 password: my-password-1 - RESULT - - Open3.stubs(:capture3).returns([ "", stderr_response, OpenStruct.new(success?: true) ]) + stub_ticks + .with("enpass-cli -json -vault vault-path show FooBar/SECRET_1") + .returns(<<~JSON) + [{"category":"computer","label":"SECRET_1","login":"","password":"my-password-1","title":"FooBar","type":"password"}] + JSON json = JSON.parse(shellunescape(run_command("fetch", "FooBar/SECRET_1"))) @@ -34,13 +34,15 @@ class EnpassAdapterTest < SecretAdapterTestCase test "fetch multiple items" do stub_ticks_with("enpass-cli version 2> /dev/null") - stderr_response = <<~RESULT - time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_1 password: my-password-1 - time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_2 password: my-password-2 - time="2024-11-03T13:34:39+01:00" level=info msg="> title: Hello login: cat.: computer label: SECRET_3 password: my-password-3 - RESULT - - Open3.stubs(:capture3).returns([ "", stderr_response, OpenStruct.new(success?: true) ]) + stub_ticks + .with("enpass-cli -json -vault vault-path show FooBar/SECRET_1 FooBar/SECRET_2") + .returns(<<~JSON) + [ + {"category":"computer","label":"SECRET_1","login":"","password":"my-password-1","title":"FooBar","type":"password"}, + {"category":"computer","label":"SECRET_2","login":"","password":"my-password-2","title":"FooBar","type":"password"}, + {"category":"computer","label":"SECRET_3","login":"","password":"my-password-1","title":"Hello","type":"password"} + ] + JSON json = JSON.parse(shellunescape(run_command("fetch", "FooBar/SECRET_1", "FooBar/SECRET_2"))) @@ -52,13 +54,15 @@ class EnpassAdapterTest < SecretAdapterTestCase test "fetch multiple items with from" do stub_ticks_with("enpass-cli version 2> /dev/null") - stderr_response = <<~RESULT - time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_1 password: my-password-1 - time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_2 password: my-password-2 - time="2024-11-03T13:34:39+01:00" level=info msg="> title: Hello login: cat.: computer label: SECRET_3 password: my-password-3 - RESULT - - Open3.stubs(:capture3).returns([ "", stderr_response, OpenStruct.new(success?: true) ]) + stub_ticks + .with("enpass-cli -json -vault vault-path show FooBar/SECRET_1 FooBar/SECRET_2") + .returns(<<~JSON) + [ + {"category":"computer","label":"SECRET_1","login":"","password":"my-password-1","title":"FooBar","type":"password"}, + {"category":"computer","label":"SECRET_2","login":"","password":"my-password-2","title":"FooBar","type":"password"}, + {"category":"computer","label":"SECRET_3","login":"","password":"my-password-1","title":"Hello","type":"password"} + ] + JSON json = JSON.parse(shellunescape(run_command("fetch", "--from", "FooBar", "SECRET_1", "SECRET_2"))) @@ -70,14 +74,16 @@ class EnpassAdapterTest < SecretAdapterTestCase test "fetch all with from" do stub_ticks_with("enpass-cli version 2> /dev/null") - stderr_response = <<~RESULT - time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_1 password: my-password-1 - time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: SECRET_2 password: my-password-2 - time="2024-11-03T13:34:39+01:00" level=info msg="> title: Hello login: cat.: computer label: SECRET_3 password: my-password-3 - time="2024-11-03T13:34:39+01:00" level=info msg="> title: FooBar login: cat.: computer label: password: my-password-3 - RESULT - - Open3.stubs(:capture3).returns([ "", stderr_response, OpenStruct.new(success?: true) ]) + stub_ticks + .with("enpass-cli -json -vault vault-path show FooBar") + .returns(<<~JSON) + [ + {"category":"computer","label":"SECRET_1","login":"","password":"my-password-1","title":"FooBar","type":"password"}, + {"category":"computer","label":"SECRET_2","login":"","password":"my-password-2","title":"FooBar","type":"password"}, + {"category":"computer","label":"SECRET_3","login":"","password":"my-password-1","title":"Hello","type":"password"}, + {"category":"computer","label":"","login":"","password":"my-password-3","title":"FooBar","type":"password"} + ] + JSON json = JSON.parse(shellunescape(run_command("fetch", "FooBar"))) From c9dec8c79a40ddb53a49de7135e440b481ac4dfe Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Thu, 7 Nov 2024 17:45:24 +0100 Subject: [PATCH 5/6] no need for open3 anymore --- lib/kamal/secrets/adapters/enpass.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/kamal/secrets/adapters/enpass.rb b/lib/kamal/secrets/adapters/enpass.rb index 773d1418d..8f11536d4 100644 --- a/lib/kamal/secrets/adapters/enpass.rb +++ b/lib/kamal/secrets/adapters/enpass.rb @@ -1,5 +1,3 @@ -require "open3" - ## # Enpass is different from most password managers, in a way that it's offline. A path to a vault is treated as account. # From 79bc7584ca36dd1b1af4110574c3c06f653260f5 Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Thu, 7 Nov 2024 18:13:37 +0100 Subject: [PATCH 6/6] make --account optional and pass Enpass vault in --from --- lib/kamal/cli/secrets.rb | 2 +- lib/kamal/secrets/adapters/enpass.rb | 25 +++++++++++++---------- test/secrets/enpass_adapter_test.rb | 30 +++------------------------- 3 files changed, 19 insertions(+), 38 deletions(-) diff --git a/lib/kamal/cli/secrets.rb b/lib/kamal/cli/secrets.rb index b094be466..0b458f13d 100644 --- a/lib/kamal/cli/secrets.rb +++ b/lib/kamal/cli/secrets.rb @@ -1,7 +1,7 @@ class Kamal::Cli::Secrets < Kamal::Cli::Base desc "fetch [SECRETS...]", "Fetch secrets from a vault" option :adapter, type: :string, aliases: "-a", required: true, desc: "Which vault adapter to use" - option :account, type: :string, required: true, desc: "The account identifier or username" + option :account, type: :string, required: false, desc: "The account identifier or username" option :from, type: :string, required: false, desc: "A vault or folder to fetch the secrets from" option :inline, type: :boolean, required: false, hidden: true def fetch(*secrets) diff --git a/lib/kamal/secrets/adapters/enpass.rb b/lib/kamal/secrets/adapters/enpass.rb index 8f11536d4..68fe41a9c 100644 --- a/lib/kamal/secrets/adapters/enpass.rb +++ b/lib/kamal/secrets/adapters/enpass.rb @@ -1,19 +1,24 @@ ## -# Enpass is different from most password managers, in a way that it's offline. A path to a vault is treated as account. +# Enpass is different from most password managers, in a way that it's offline and doesn't need an account. # -# Pass it like so: `kamal secrets fetch --adapter enpass --account /Users/YOUR_USERNAME/Library/Containers/in.sinew.Enpass-Desktop/Data/Documents/Vaults/primary --from MY_PROD_SERVER` +# Usage +# +# Fetch all password from FooBar item +# `kamal secrets fetch --adapter enpass --from /Users/YOUR_USERNAME/Library/Containers/in.sinew.Enpass-Desktop/Data/Documents/Vaults/primary FooBar` +# +# Fetch only DB_PASSWORD from FooBar item +# `kamal secrets fetch --adapter enpass --from /Users/YOUR_USERNAME/Library/Containers/in.sinew.Enpass-Desktop/Data/Documents/Vaults/primary FooBar/DB_PASSWORD` class Kamal::Secrets::Adapters::Enpass < Kamal::Secrets::Adapters::Base - private - def login(account) - # There is no concept of session in enpass-cli - true - end + def fetch(secrets, account: nil, from:) + check_dependencies! + fetch_secrets(secrets, from) + end - def fetch_secrets(secrets, account:, session:) + private + def fetch_secrets(secrets, vault) secrets_titles = fetch_secret_titles(secrets) - # Enpass outputs result as stderr, I did not find a way to stub backticks and output to stderr. Open3 did the job. - result = `enpass-cli -json -vault #{account.shellescape} show #{secrets.map(&:shellescape).join(" ")}`.strip + result = `enpass-cli -json -vault #{vault.shellescape} show #{secrets_titles.map(&:shellescape).join(" ")}`.strip parse_result_and_take_secrets(result, secrets) end diff --git a/test/secrets/enpass_adapter_test.rb b/test/secrets/enpass_adapter_test.rb index 852c7c9bf..edc49613c 100644 --- a/test/secrets/enpass_adapter_test.rb +++ b/test/secrets/enpass_adapter_test.rb @@ -1,10 +1,6 @@ require "test_helper" class EnpassAdapterTest < SecretAdapterTestCase - setup do - `true` # Ensure $? is 0 - end - test "fetch without CLI installed" do stub_ticks_with("enpass-cli version 2> /dev/null", succeed: false) @@ -19,7 +15,7 @@ class EnpassAdapterTest < SecretAdapterTestCase stub_ticks_with("enpass-cli version 2> /dev/null") stub_ticks - .with("enpass-cli -json -vault vault-path show FooBar/SECRET_1") + .with("enpass-cli -json -vault vault-path show FooBar") .returns(<<~JSON) [{"category":"computer","label":"SECRET_1","login":"","password":"my-password-1","title":"FooBar","type":"password"}] JSON @@ -35,7 +31,7 @@ class EnpassAdapterTest < SecretAdapterTestCase stub_ticks_with("enpass-cli version 2> /dev/null") stub_ticks - .with("enpass-cli -json -vault vault-path show FooBar/SECRET_1 FooBar/SECRET_2") + .with("enpass-cli -json -vault vault-path show FooBar") .returns(<<~JSON) [ {"category":"computer","label":"SECRET_1","login":"","password":"my-password-1","title":"FooBar","type":"password"}, @@ -51,26 +47,6 @@ class EnpassAdapterTest < SecretAdapterTestCase assert_equal expected_json, json end - test "fetch multiple items with from" do - stub_ticks_with("enpass-cli version 2> /dev/null") - - stub_ticks - .with("enpass-cli -json -vault vault-path show FooBar/SECRET_1 FooBar/SECRET_2") - .returns(<<~JSON) - [ - {"category":"computer","label":"SECRET_1","login":"","password":"my-password-1","title":"FooBar","type":"password"}, - {"category":"computer","label":"SECRET_2","login":"","password":"my-password-2","title":"FooBar","type":"password"}, - {"category":"computer","label":"SECRET_3","login":"","password":"my-password-1","title":"Hello","type":"password"} - ] - JSON - - json = JSON.parse(shellunescape(run_command("fetch", "--from", "FooBar", "SECRET_1", "SECRET_2"))) - - expected_json = { "FooBar/SECRET_1" => "my-password-1", "FooBar/SECRET_2" => "my-password-2" } - - assert_equal expected_json, json - end - test "fetch all with from" do stub_ticks_with("enpass-cli version 2> /dev/null") @@ -99,7 +75,7 @@ def run_command(*command) [ *command, "-c", "test/fixtures/deploy_with_accessories.yml", "--adapter", "enpass", - "--account", "vault-path" ] + "--from", "vault-path" ] end end end