Skip to content

Commit 5fe0683

Browse files
committed
Add "--value-file" option to "sops set [...]"
This allows running "sops set [...]" without leaking secrets in process listings. To read secrets from stdin, use "/dev/stdin" as the file path. Fixes #729 Signed-off-by: Bjørn Forsman <bjorn.forsman@gmail.com>
1 parent 518daad commit 5fe0683

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

cmd/sops/main.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,10 @@ func main() {
13851385
Name: "output-type",
13861386
Usage: "currently json, yaml, dotenv and binary are supported. If not set, sops will use the input file's extension to determine the output format",
13871387
},
1388+
cli.BoolFlag{
1389+
Name: "value-file",
1390+
Usage: "treat 'value' as a file to read the actual value from (avoids leaking secrets in process listings)",
1391+
},
13881392
cli.IntFlag{
13891393
Name: "shamir-secret-sharing-threshold",
13901394
Usage: "the number of master keys required to retrieve the data key with shamir",
@@ -1430,7 +1434,18 @@ func main() {
14301434
return common.NewExitError("Invalid set index format", codes.ErrorInvalidSetFormat)
14311435
}
14321436

1433-
value, err := jsonValueToTreeInsertableValue(c.Args()[2])
1437+
var data string
1438+
if c.Bool("value-file") {
1439+
filename := c.Args()[2]
1440+
content, err := os.ReadFile(filename)
1441+
if err != nil {
1442+
return toExitError(err)
1443+
}
1444+
data = string(content)
1445+
} else {
1446+
data = c.Args()[2]
1447+
}
1448+
value, err := jsonValueToTreeInsertableValue(data)
14341449
if err != nil {
14351450
return toExitError(err)
14361451
}

functional-tests/src/lib.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,52 @@ bar: baz",
490490
panic!("Output JSON does not have the expected structure");
491491
}
492492

493+
#[test]
494+
fn set_json_file_insert_with_value_file() {
495+
let file_path =
496+
prepare_temp_file("test_set_insert.json", r#"{"a": 2, "b": "ba"}"#.as_bytes());
497+
let value_file = prepare_temp_file("value_file.json", r#"{"cc": "ccc"}"#.as_bytes());
498+
assert!(
499+
Command::new(SOPS_BINARY_PATH)
500+
.arg("encrypt")
501+
.arg("-i")
502+
.arg(file_path.clone())
503+
.output()
504+
.expect("Error running sops")
505+
.status
506+
.success(),
507+
"sops didn't exit successfully"
508+
);
509+
let output = Command::new(SOPS_BINARY_PATH)
510+
.arg("set")
511+
.arg("--value-file")
512+
.arg(file_path.clone())
513+
.arg(r#"["c"]"#)
514+
.arg(value_file.clone())
515+
.output()
516+
.expect("Error running sops");
517+
println!(
518+
"stdout: {}, stderr: {}",
519+
String::from_utf8_lossy(&output.stdout),
520+
String::from_utf8_lossy(&output.stderr)
521+
);
522+
assert!(output.status.success(), "sops didn't exit successfully");
523+
let mut s = String::new();
524+
File::open(file_path)
525+
.unwrap()
526+
.read_to_string(&mut s)
527+
.unwrap();
528+
let data: Value = serde_json::from_str(&s).expect("Error parsing sops's JSON output");
529+
if let Value::Mapping(data) = data {
530+
let a = data.get(&Value::String("c".to_owned())).unwrap();
531+
if let &Value::Mapping(ref a) = a {
532+
assert_encrypted!(&a, Value::String("cc".to_owned()));
533+
return;
534+
}
535+
}
536+
panic!("Output JSON does not have the expected structure");
537+
}
538+
493539
#[test]
494540
fn set_yaml_file_update() {
495541
let file_path = prepare_temp_file(

0 commit comments

Comments
 (0)