Skip to content

Commit a8ececb

Browse files
committed
unlock: base64 arg by default and no file input
- Remove the syntax that was allowing to provide the key from a file. - Make the default syntax be for passing the key as base64 directly as argument (without requiring the `base64:` prefix for that case anymore. Rationale: It was never a good idea to provide a built-in way to read the binary key from a file IMHO, because that would suggest that having the key laying around in a random file on disk was a good idea to begin with (which can be OK… as long as the key file is properly protected). For example, we wouldn't want to incite people to write the key in a file just to be able to `git-conceal unlock` with it… and then accidentally commit that key file. Reading from a file is still possible using `-` as the argument to read from `stdin`, then using shell redirection syntax `<file` to feed the content of the file as stdin. So even in the unlikely case that someone would want to provide the key via a file, they can still do that. And since having the base64 key directly will be the most common use case for `unlock` on local machines (when developers get the key from the secret store and copy/paste it to the `unlock` command), that feels more fitting for this to be the default command / most basic syntax (i.e. without requiring a `base64:` prefix for it)
1 parent b4a4afc commit a8ececb

File tree

4 files changed

+35
-48
lines changed

4 files changed

+35
-48
lines changed

README.md

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -141,24 +141,23 @@ This will show the raw content as stored in the repository. So even if `cat my-s
141141
After you freshly clone a repository which contains files which have been encrypted by `git-conceal`, you need to provide the symmetric key that your coworkers would have shared with you to decrypt it:
142142

143143
```bash
144-
# Option 1: Provide the key via an environment variable (base64 encoded).
145-
# Recommended on CI, where secret values like the key are usually exposed to jobs as env vars.
146-
$ git-conceal unlock env:GIT_CONCEAL_SECRET_KEY
147-
148-
# Option 2: Provide the Base64-encoded key as command line argument.
144+
# Option 1: Provide the Base64-encoded key directly as command line argument.
149145
# Only use locally, as on CI this could leak the key in logs.
150146
# Tip: start your command with a space to avoid it (and thus the key) being added to your shell's history
151-
$ git-conceal unlock "base64:c3VwcG9zZWRseS15b3VyLWJpbmFyeS1zZWNyZXRrZXk="
147+
$ git-conceal unlock "c3VwcG9zZWRseS15b3VyLWJpbmFyeS1zZWNyZXRrZXk="
152148

153-
# Option 3: Provide a path to a from file containing the raw binary, 32 bytes key.
154-
$ git-conceal unlock /path/to/key.bin
149+
# Option 2: Provide the key via an environment variable (base64 encoded), by using the `env:` prefix.
150+
# Recommended on CI, where secret values like the key are usually exposed to jobs as env vars.
151+
# Prefer this over `git-conceal unlock $GIT_CONCEAL_SECRET_KEY` to reduce the risk of accidentally leaking
152+
# the key e.g. in CI logs (which might resolve `$VAR` env vars before printing the resolved command in logs)
153+
$ git-conceal unlock env:GIT_CONCEAL_SECRET_KEY
155154

156-
# Option 4: Provide it via stdin (expects raw binary, 32 bytes as input)
157-
$ cat /path/to/key.bin | git-conceal unlock -
158-
# Or convert from base64.
159-
# Only use locally, as on CI this could leak the key in logs.
160-
# Tip: start your command with a space to avoid it (and thus the key) being added to your shell's history
161-
$ echo "c3VwcG9zZWRseS15b3VyLWJpbmFyeS1zZWNyZXRrZXk=" | base64 -d | git-conceal unlock -
155+
# Option 3: Provide it via stdin (expects raw binary, 32 bytes as input)
156+
# For example, if you have the binary key in a file (which you would hopefully have protected properly!)
157+
$ git-conceal unlock - </path/to/keyfile.bin
158+
# Or if you have the base64-encoded key in your clipboard, you could do:
159+
$ pbpaste | base64 -d | git-conceal unlock -
160+
# (Though in that case `git-conceal unlock $(pbpaste)` would achieve the same thing)
162161
```
163162

164163
This will:

src/commands/init.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,14 @@ fn init_instructions(key_b64: &str) -> String {
4343
{key_b64}
4444
4545
Once you share this key with users you trust, they can unlock their working copy using one of these methods:
46+
- From base64-encoded key passed directly as argument:
47+
{bin_name} unlock "{key_b64}"
4648
- From environment variable (base64):
47-
export GIT_SECRETS_KEY='{key_b64}'
48-
{bin_name} unlock env:GIT_SECRETS_KEY
49-
- From base64-encoded key in the command line:
50-
{bin_name} unlock "base64:{key_b64}"
51-
- From file (raw binary, 32 bytes):
52-
{bin_name} unlock /path/to/key.bin
49+
export GIT_CONCEAL_SECRET_KEY='{key_b64}'
50+
{bin_name} unlock env:GIT_CONCEAL_SECRET_KEY
5351
- From stdin (raw binary, 32 bytes):
5452
echo '{key_b64}' | base64 -d | {bin_name} unlock -
53+
{bin_name} unlock - < /path/to/raw-binary-key.bin
5554
5655
To start adding files to be encrypted in this repository:
5756
- List files (or file patterns) you want to encrypt in your `.gitattributes` file, like this:

src/key.rs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,9 @@ impl Key {
7272
/// Read encryption key from various sources
7373
///
7474
/// Supports:
75-
/// - `"-"` for reading from stdin (raw binary format, 32 bytes)
75+
/// - Base64-encoded key string (passed directly as argument)
7676
/// - `"env:VARNAME"` for reading from environment variable (base64 encoded)
77-
/// - `"base64:BASE64_KEY"` for reading from base64-encoded key
78-
/// - File path for reading from a file (raw binary format, 32 bytes)
77+
/// - `"-"` for reading from stdin (raw binary format, 32 bytes)
7978
///
8079
/// Returns the encryption key.
8180
pub fn read_from_source(key_source: &str) -> Result<Self> {
@@ -95,11 +94,9 @@ impl Key {
9594
format!("Failed to read key from environment variable {}", env_var)
9695
})?;
9796
Self::from_base64(&key_b64)
98-
} else if let Some(base64_key) = key_source.strip_prefix("base64:") {
99-
Self::from_base64(base64_key)
10097
} else {
101-
// Read from file (raw binary format)
102-
Self::from_file(Path::new(key_source))
98+
// Treat as base64-encoded key (unprefixed)
99+
Self::from_base64(key_source)
103100
}
104101
}
105102
}
@@ -301,18 +298,6 @@ mod tests {
301298
assert!(result.unwrap_err().to_string().contains("Invalid key size"));
302299
}
303300

304-
#[test]
305-
fn test_read_from_source_file() {
306-
let temp_dir = TempDir::new().unwrap();
307-
let key_file = temp_dir.path().join("test.key");
308-
309-
let key = test_key();
310-
fs::write(&key_file, key.as_bytes()).unwrap();
311-
312-
let loaded_key = Key::read_from_source(key_file.to_str().unwrap()).unwrap();
313-
assert_eq!(loaded_key.as_bytes(), key.as_bytes());
314-
}
315-
316301
/// This test must run serially (not in parallel with other tests) because it modifies
317302
/// environment variables. Environment variable modification is not thread-safe and can
318303
/// cause race conditions when tests run in parallel.
@@ -398,18 +383,23 @@ mod tests {
398383
fn test_read_from_source_base64() {
399384
let key = test_key();
400385
let b64 = key.to_base64();
401-
let loaded_key = Key::read_from_source(&format!("base64:{}", b64)).unwrap();
386+
let loaded_key = Key::read_from_source(&b64).unwrap();
402387
assert_eq!(loaded_key.as_bytes(), key.as_bytes());
403388
}
404389

405390
// Note: Testing stdin reading is complex in unit tests as it requires
406391
// mocking stdin or using a separate process. This would be better suited
407-
// for integration tests. The file and env var cases are tested above.
392+
// for integration tests. The env var and base64 cases are tested above.
408393

409394
#[test]
410-
fn test_read_from_source_invalid_source() {
411-
let result = Key::read_from_source("/nonexistent/path/key.bin");
395+
fn test_read_from_source_invalid_base64() {
396+
// A user accidentally passing a file path should be treated as base64 key and fail to decode
397+
let result = Key::read_from_source("path/to/secret-symmetric-key.bin");
412398
assert!(result.is_err());
399+
assert!(result
400+
.unwrap_err()
401+
.to_string()
402+
.contains("Failed to decode base64 key"));
413403
}
414404

415405
#[test]

src/main.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,10 @@ enum Commands {
5050
Unlock {
5151
/// Key source
5252
#[arg(
53-
value_name = "KEY_SOURCE",
54-
long_help = "- 'env:VARNAME': base64-encoded key in environment variable (recommended on CI)\n\
55-
- 'base64:BASE64_KEY': base64-encoded key\n\
56-
- '-': raw binary key from stdin\n\
57-
- <PATH>: raw binary key from file"
53+
value_name = "KEY",
54+
long_help = "- 'BASE64KEY': the base64-encoded key passed directly as argument\n\
55+
- 'env:VARNAME': read the base64-encoded key from the given environment variable (recommended on CI)\n\
56+
- '-': read the raw binary key from stdin (expects raw binary, 32 bytes as input)"
5857
)]
5958
key_source: String,
6059
},

0 commit comments

Comments
 (0)