From 062714df8fba88a9d7a3c418388b07bdb92cc8cf Mon Sep 17 00:00:00 2001 From: Kuntal Majumder Date: Wed, 24 Oct 2018 01:21:25 +0530 Subject: [PATCH 1/7] Added an output flag for saving the output to a specified file --- cmd/sops/main.go | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/cmd/sops/main.go b/cmd/sops/main.go index 42010854c..9fbc34743 100644 --- a/cmd/sops/main.go +++ b/cmd/sops/main.go @@ -1,24 +1,18 @@ package main //import "go.mozilla.org/sops/cmd/sops" import ( + encodingjson "encoding/json" + "fmt" "net" "net/url" - - "google.golang.org/grpc" - - "go.mozilla.org/sops" - - "fmt" "os" - "strings" - "time" - - encodingjson "encoding/json" "reflect" - "strconv" + "strings" + "time" "github.com/sirupsen/logrus" + "go.mozilla.org/sops" "go.mozilla.org/sops/aes" _ "go.mozilla.org/sops/audit" "go.mozilla.org/sops/azkv" @@ -36,6 +30,7 @@ import ( "go.mozilla.org/sops/pgp" "go.mozilla.org/sops/stores/json" yamlstores "go.mozilla.org/sops/stores/yaml" + "google.golang.org/grpc" "gopkg.in/urfave/cli.v1" ) @@ -407,6 +402,10 @@ func main() { Name: "verbose", Usage: "Enable verbose logging output", }, + cli.StringFlag{ + Name: "output", + Usage: "Save the output after encryption or decryption to the file specified", + }, }, keyserviceFlags...) app.Action = func(c *cli.Context) error { @@ -419,7 +418,7 @@ func main() { fileName := c.Args()[0] if _, err := os.Stat(fileName); os.IsNotExist(err) { if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-azure-kv") != "" || - c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-azure-kv") != "" { + c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-azure-kv") != "" || c.String("output") != "" { return common.NewExitError("Error: cannot add or remove keys on non-existent files, use `--kms` and `--pgp` instead.", codes.CannotChangeKeysFromNonExistentFile) } if c.Bool("encrypt") || c.Bool("decrypt") || c.Bool("rotate") { @@ -619,6 +618,19 @@ func main() { log.Info("File written successfully") return nil } + + if c.String("output") != "" { + file, err := os.Create(c.String("output")) + if err != nil { + return common.NewExitError(fmt.Sprintf("Could not open output file for writing: %s", err), codes.CouldNotWriteOutputFile) + } + defer file.Close() + _, err = file.Write(output) + if err != nil { + return toExitError(err) + } + log.Info("Output File written successfully") + } _, err = os.Stdout.Write(output) return toExitError(err) } From 1731966a2658d85a689a378c30a19a6bf01b63ad Mon Sep 17 00:00:00 2001 From: Kuntal Majumder Date: Wed, 24 Oct 2018 23:04:13 +0530 Subject: [PATCH 2/7] Removed unnecessary condition Rewrote output condition to something more idiomatic Go code --- cmd/sops/main.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cmd/sops/main.go b/cmd/sops/main.go index 9fbc34743..acf150f28 100644 --- a/cmd/sops/main.go +++ b/cmd/sops/main.go @@ -418,7 +418,7 @@ func main() { fileName := c.Args()[0] if _, err := os.Stat(fileName); os.IsNotExist(err) { if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-azure-kv") != "" || - c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-azure-kv") != "" || c.String("output") != "" { + c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-azure-kv") != "" { return common.NewExitError("Error: cannot add or remove keys on non-existent files, use `--kms` and `--pgp` instead.", codes.CannotChangeKeysFromNonExistentFile) } if c.Bool("encrypt") || c.Bool("decrypt") || c.Bool("rotate") { @@ -619,19 +619,16 @@ func main() { return nil } + outputFile := os.Stdout if c.String("output") != "" { file, err := os.Create(c.String("output")) if err != nil { return common.NewExitError(fmt.Sprintf("Could not open output file for writing: %s", err), codes.CouldNotWriteOutputFile) } defer file.Close() - _, err = file.Write(output) - if err != nil { - return toExitError(err) - } - log.Info("Output File written successfully") + outputFile = file } - _, err = os.Stdout.Write(output) + _, err = outputFile.Write(output) return toExitError(err) } app.Run(os.Args) From 6ee5d9d60d8e70d41cf72b09f3a9a5be74bd82ae Mon Sep 17 00:00:00 2001 From: Kuntal Majumder Date: Wed, 24 Oct 2018 23:11:17 +0530 Subject: [PATCH 3/7] Added check when --output and --in-file is used at the same time --- cmd/sops/main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/sops/main.go b/cmd/sops/main.go index acf150f28..16ea6f10e 100644 --- a/cmd/sops/main.go +++ b/cmd/sops/main.go @@ -415,6 +415,9 @@ func main() { if c.NArg() < 1 { return common.NewExitError("Error: no file specified", codes.NoFileSpecified) } + if c.Bool("in-place") && c.String("output") != "" { + return common.NewExitError("Error: cannot operate on both --output and --in-file", codes.ErrorConflictingParameters) + } fileName := c.Args()[0] if _, err := os.Stat(fileName); os.IsNotExist(err) { if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-azure-kv") != "" || From f51dd00fd1c5a74259290306d53993f483ce3ff6 Mon Sep 17 00:00:00 2001 From: Kuntal Majumder Date: Wed, 24 Oct 2018 23:40:48 +0530 Subject: [PATCH 4/7] Updated docs with the --ouput flag description --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index 8641b8fd5..19ef3c129 100644 --- a/README.rst +++ b/README.rst @@ -720,6 +720,12 @@ provide more than one backend, and SOPS will log to all of them: - connection_string: "postgres://sops:sops@localhost/sops?sslmode=verify-full" - connection_string: "postgres://sops:sops@remotehost/sops?sslmode=verify-full" +Saving Output to a File +~~~~~~~~~~~~~~~~~~~~~~~ +By default ``sops`` just dumps all the output to the standard output. We can use the +``--output`` flag followed by a filename to save the output to the file specified. +Beware using both ``--in-place`` and ``--output`` flags will result in an error. + Important information on types ------------------------------ From 9e3cf89a52e9a7a39641322dec37df4e054f9383 Mon Sep 17 00:00:00 2001 From: Kuntal Majumder Date: Thu, 25 Oct 2018 12:34:31 +0530 Subject: [PATCH 5/7] Changed --in-place to --in-file --- cmd/sops/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/sops/main.go b/cmd/sops/main.go index 16ea6f10e..0638b9593 100644 --- a/cmd/sops/main.go +++ b/cmd/sops/main.go @@ -415,7 +415,7 @@ func main() { if c.NArg() < 1 { return common.NewExitError("Error: no file specified", codes.NoFileSpecified) } - if c.Bool("in-place") && c.String("output") != "" { + if c.Bool("in-file") && c.String("output") != "" { return common.NewExitError("Error: cannot operate on both --output and --in-file", codes.ErrorConflictingParameters) } fileName := c.Args()[0] From 2916f534310692c7174831026bbe55d17fdd33fd Mon Sep 17 00:00:00 2001 From: Kuntal Majumder Date: Thu, 25 Oct 2018 17:05:59 +0530 Subject: [PATCH 6/7] Typo fixed : changed --in-file to --in-place --- cmd/sops/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/sops/main.go b/cmd/sops/main.go index 0638b9593..5740e893a 100644 --- a/cmd/sops/main.go +++ b/cmd/sops/main.go @@ -415,8 +415,8 @@ func main() { if c.NArg() < 1 { return common.NewExitError("Error: no file specified", codes.NoFileSpecified) } - if c.Bool("in-file") && c.String("output") != "" { - return common.NewExitError("Error: cannot operate on both --output and --in-file", codes.ErrorConflictingParameters) + if c.Bool("in-place") && c.String("output") != "" { + return common.NewExitError("Error: cannot operate on both --output and --in-place", codes.ErrorConflictingParameters) } fileName := c.Args()[0] if _, err := os.Stat(fileName); os.IsNotExist(err) { From 6ce0290791c8451fbc83f7a4e5a1e1a23157391f Mon Sep 17 00:00:00 2001 From: Adrian Utrilla Date: Wed, 7 Nov 2018 08:09:44 -0500 Subject: [PATCH 7/7] Add functional test for --output flag --- functional-tests/src/lib.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/functional-tests/src/lib.rs b/functional-tests/src/lib.rs index 65afb2c86..7f0f60a73 100644 --- a/functional-tests/src/lib.rs +++ b/functional-tests/src/lib.rs @@ -18,6 +18,7 @@ mod tests { use tempdir::TempDir; use std::process::Command; use serde_yaml::Value; + use std::path::Path; const SOPS_BINARY_PATH: &'static str = "./sops"; macro_rules! assert_encrypted { @@ -409,4 +410,26 @@ b: ba"# assert_eq!(output.stdout, data); } + #[test] + fn output_flag() { + let input_path = prepare_temp_file("test_output_flag.binary", b"foo"); + let output_path = Path::join(TMP_DIR.path(), "output_flag.txt"); + let output = Command::new(SOPS_BINARY_PATH) + .arg("--output") + .arg(&output_path) + .arg("-e") + .arg(input_path.clone()) + .output() + .expect("Error running sops"); + assert!(output.status + .success(), + "SOPS failed to decrypt a binary file"); + assert_eq!(output.stdout, &[]); + let mut f = File::open(&output_path).expect("output file not found"); + + let mut contents = String::new(); + f.read_to_string(&mut contents) + .expect("couldn't read output file contents"); + assert_ne!(contents, "", "Output file is empty"); + } }