diff --git a/secrets b/secrets index 5711842..421fc5f 100755 --- a/secrets +++ b/secrets @@ -135,13 +135,13 @@ _validate_recipient_name() { RECIPIENT_ARGS=() _load_recipients() { RECIPIENT_ARGS=() + if [ -L "$RECIPIENTS_FILE" ]; then + die "Refusing to read symlinked $RECIPIENTS_FILE_NAME (security)." + fi if [ ! -e "$RECIPIENTS_FILE" ]; then RECIPIENT_ARGS=(-r "$(get_pubkey)") return 0 fi - if [ -L "$RECIPIENTS_FILE" ]; then - die "Refusing to read symlinked $RECIPIENTS_FILE_NAME (security)." - fi local line trimmed n=0 while IFS= read -r line || [ -n "$line" ]; do trimmed="${line#"${line%%[![:space:]]*}"}" # lstrip diff --git a/test/recipients.bats b/test/recipients.bats index c02888c..ae13b22 100644 --- a/test/recipients.bats +++ b/test/recipients.bats @@ -290,3 +290,62 @@ make_second_identity() { [ "$status" -ne 0 ] [[ "$output" == *"recipient"* ]] || false } + +@test "SECURITY: a dangling symlink recipients.txt is refused, not silently ignored" { + init_with_remote + rm -f "$SECRETS_DIR/recipients.txt" + ln -s "$TEST_TMPDIR/does-not-exist.txt" "$SECRETS_DIR/recipients.txt" + create_project_dir myproj + run "$SECRETS_BIN" push + [ "$status" -ne 0 ] + [[ "$output" == *"symlink"* ]] || false +} + +@test "SECURITY: recipients.txt with shell metacharacters is rejected, no execution" { + init_with_remote + STORE_PUB=$(age-keygen -y "$SECRETS_DIR/key.txt") + printf '%s\nage1$(touch %s/pwned)\n' "$STORE_PUB" "$TEST_TMPDIR" > "$SECRETS_DIR/recipients.txt" + create_project_dir myproj + run "$SECRETS_BIN" push + [ "$status" -ne 0 ] + [ ! -e "$TEST_TMPDIR/pwned" ] + [[ "$output" == *"Invalid recipient"* ]] || false +} + +@test "SECURITY: recipients.txt line that looks like an extra age flag is rejected" { + init_with_remote + STORE_PUB=$(age-keygen -y "$SECRETS_DIR/key.txt") + printf '%s\n-i /etc/passwd\n' "$STORE_PUB" > "$SECRETS_DIR/recipients.txt" + create_project_dir myproj + run "$SECRETS_BIN" push + [ "$status" -ne 0 ] + [[ "$output" == *"Invalid recipient"* ]] || false +} + +@test "SECURITY: control/ANSI characters in recipients.txt are rejected" { + init_with_remote + STORE_PUB=$(age-keygen -y "$SECRETS_DIR/key.txt") + printf '%s\nage1%b\n' "$STORE_PUB" 'aaaa\033[31mevil' > "$SECRETS_DIR/recipients.txt" + create_project_dir myproj + run "$SECRETS_BIN" push + [ "$status" -ne 0 ] +} + +@test "SECURITY: recipients add rejects a key with embedded whitespace" { + init_with_remote + run "$SECRETS_BIN" recipients add "age1aaaa bbbb" + [ "$status" -ne 0 ] + [[ "$output" == *"valid age recipient"* ]] || false +} + +@test "SECURITY: a symlinked recipients.txt is refused on add and rm too" { + init_with_remote + make_second_identity + STORE_PUB=$(age-keygen -y "$SECRETS_DIR/key.txt") + printf '%s\n' "$STORE_PUB" > "$TEST_TMPDIR/elsewhere.txt" + rm -f "$SECRETS_DIR/recipients.txt" + ln -s "$TEST_TMPDIR/elsewhere.txt" "$SECRETS_DIR/recipients.txt" + run "$SECRETS_BIN" recipients add "$BOB_PUB" + [ "$status" -ne 0 ] + [[ "$output" == *"symlink"* ]] || false +}