From 75594616b70981fe2759ec2b3e5c06408544361c Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Wed, 10 Dec 2025 21:57:09 +0100 Subject: [PATCH 1/3] Add tip about `histignorespace` in README h/t @iangmaia in https://github.com/Automattic/git-conceal/pull/10#discussion_r2607931229 --- README.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f3a2db1..c776046 100644 --- a/README.md +++ b/README.md @@ -141,20 +141,24 @@ This will show the raw content as stored in the repository. So even if `cat my-s 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: ```bash -# Option 1: Provide the key via an environment variable (base64 encoded). Recommended on CI. -export GIT_SECRETS_KEY="YOUR_BASE64_KEY" -git-conceal unlock env:GIT_SECRETS_KEY +# Option 1: Provide the key via an environment variable (base64 encoded). +# Recommended on CI, where secret values like the key are usually exposed to jobs as env vars. +$ git-conceal unlock env:GIT_CONCEAL_SECRET_KEY -# Option 2: Provide the Base64-encoded key as command line argument. (Only use locally, as on CI this could leak the key in logs). -git-conceal unlock "base64:c3VwcG9zZWRseS15b3VyLWJpbmFyeS1zZWNyZXRrZXk=" +# Option 2: Provide the Base64-encoded key as command line argument. +# Only use locally, as on CI this could leak the key in logs. +# Tip: start your command with a space to avoid it (and thus the key) being added to your shell's history +$ git-conceal unlock "base64:c3VwcG9zZWRseS15b3VyLWJpbmFyeS1zZWNyZXRrZXk=" # Option 3: Provide a path to a from file containing the raw binary, 32 bytes key. -git-conceal unlock /path/to/key.bin +$ git-conceal unlock /path/to/key.bin # Option 4: Provide it via stdin (expects raw binary, 32 bytes as input) -cat /path/to/key.bin | git-conceal unlock - -# Or convert from base64. (Only use locally, as on CI this could leak the key in logs). -echo "c3VwcG9zZWRseS15b3VyLWJpbmFyeS1zZWNyZXRrZXk=" | base64 -d | git-conceal unlock - +$ cat /path/to/key.bin | git-conceal unlock - +# Or convert from base64. +# Only use locally, as on CI this could leak the key in logs. +# Tip: start your command with a space to avoid it (and thus the key) being added to your shell's history +$ echo "c3VwcG9zZWRseS15b3VyLWJpbmFyeS1zZWNyZXRrZXk=" | base64 -d | git-conceal unlock - ``` This will: From b09075d60b0b27b4553f103bbe17c9c35a21c9ad Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Wed, 10 Dec 2025 22:47:04 +0100 Subject: [PATCH 2/3] `unlock`: base64 arg by default and no file input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 ` Result { @@ -95,11 +94,9 @@ impl Key { format!("Failed to read key from environment variable {}", env_var) })?; Self::from_base64(&key_b64) - } else if let Some(base64_key) = key_source.strip_prefix("base64:") { - Self::from_base64(base64_key) } else { - // Read from file (raw binary format) - Self::from_file(Path::new(key_source)) + // Treat as base64-encoded key (unprefixed) + Self::from_base64(key_source) } } } @@ -301,18 +298,6 @@ mod tests { assert!(result.unwrap_err().to_string().contains("Invalid key size")); } - #[test] - fn test_read_from_source_file() { - let temp_dir = TempDir::new().unwrap(); - let key_file = temp_dir.path().join("test.key"); - - let key = test_key(); - fs::write(&key_file, key.as_bytes()).unwrap(); - - let loaded_key = Key::read_from_source(key_file.to_str().unwrap()).unwrap(); - assert_eq!(loaded_key.as_bytes(), key.as_bytes()); - } - /// This test must run serially (not in parallel with other tests) because it modifies /// environment variables. Environment variable modification is not thread-safe and can /// cause race conditions when tests run in parallel. @@ -398,18 +383,23 @@ mod tests { fn test_read_from_source_base64() { let key = test_key(); let b64 = key.to_base64(); - let loaded_key = Key::read_from_source(&format!("base64:{}", b64)).unwrap(); + let loaded_key = Key::read_from_source(&b64).unwrap(); assert_eq!(loaded_key.as_bytes(), key.as_bytes()); } // Note: Testing stdin reading is complex in unit tests as it requires // mocking stdin or using a separate process. This would be better suited - // for integration tests. The file and env var cases are tested above. + // for integration tests. The env var and base64 cases are tested above. #[test] - fn test_read_from_source_invalid_source() { - let result = Key::read_from_source("/nonexistent/path/key.bin"); + fn test_read_from_source_invalid_base64() { + // A user accidentally passing a file path should be treated as base64 key and fail to decode + let result = Key::read_from_source("path/to/secret-symmetric-key.bin"); assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains("Failed to decode base64 key")); } #[test] diff --git a/src/main.rs b/src/main.rs index 0b34470..e944ba8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,11 +50,10 @@ enum Commands { Unlock { /// Key source #[arg( - value_name = "KEY_SOURCE", - long_help = "- 'env:VARNAME': base64-encoded key in environment variable (recommended on CI)\n\ - - 'base64:BASE64_KEY': base64-encoded key\n\ - - '-': raw binary key from stdin\n\ - - : raw binary key from file" + value_name = "KEY", + long_help = "- 'BASE64KEY': the base64-encoded key passed directly as argument\n\ + - 'env:VARNAME': read the base64-encoded key from the given environment variable (recommended on CI)\n\ + - '-': read the raw binary key from stdin (expects raw binary, 32 bytes as input)" )] key_source: String, }, From bf95874367b039ff6217c0a8206472f308d3a6b5 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Thu, 11 Dec 2025 01:05:42 +0100 Subject: [PATCH 3/3] README typos and wording tweaks --- README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2315502..474e02c 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ This will show the raw content as stored in the repository. So even if `cat my-s ### Unlock a repository -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: +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: ```bash # Option 1: Provide the Base64-encoded key directly as command line argument. @@ -189,7 +189,7 @@ git-conceal status This shows: - Whether the repository is locked or unlocked - Whether filters are configured -- Which file patterns are encrypted (from `.gitattributes`) +- Which files are handled by `git-conceal` (i.e. tracked files matching one of the patterns with `filter=git-conceal` in your `.gitattributes`) ```bash git-conceal status @@ -208,7 +208,7 @@ git-conceal key show ### Rotate the encryption key There are times when you might need to rotate the encryption key used in an encrypted repository. -For example, in the unfortunate even of the key leaking or when a coworker leaves your team/company and you want to ensure they can't access new secrets. +For example, in the unfortunate event of the key leaking, or when a coworker leaves your team/company and you want to ensure they can't access new secrets. You can rotate the encryption key with: @@ -248,15 +248,19 @@ For detailed security information, including key management, deterministic encry ## New releases -Releases are automated by our CI every time we make a `git tag` on the repo. Be sure to update the version in the `Cargo.toml` first though. +Releases are automated by our CI every time we make a `git tag` on the repo. + +
Release instructions for maintainers - Create a `release/x.y.z` branch - Edit `Cargo.toml` to update the `version = "x.y.z"` field - - Run `cargo check` to update the `Cargo.lock` and validate the code still compiles + - Run `cargo check` to update the `Cargo.lock` with the new version and validate the code still compiles - `git add Cargo.toml Cargo.lock` then `git commit -m "Bump version to x.y.z"` - - Create a PR and get it merged + - Create a PR with those changes and get it merged into `trunk` - Once it has landed in `trunk`, push a new tag (`git tag "x.y.z"` then `git push origin "x.y.z"`) - - Then let the CI build the release binaries for all platforms, create the GitHub Release, and attach the compiled binaries as assets. + +The CI will trigger on the Git tag and take care of building the release binaries for all platforms and creating the GitHub Release with those binaries attached as assets. +
## License