Summary
shb.replaceSecrets currently routes secret contents through shell command substitution and then through command-line arguments. This is fragile for correctness and security.
#720 improves multiline secret handling by replacing sed with a Python replacement helper, fixing the immediate multiline substitution failure. However, the broader design still passes secret values as CLI arguments, which means secret contents are not handled as opaque file data.
Problem
Secret values are effectively read with shell command substitution, e.g. conceptually:
and then passed as arguments to the replacement command.
This has several consequences:
- Trailing newlines are stripped by shell command substitution.
- NUL bytes cannot be represented in shell strings / argv.
- Secret values can be exposed through the process list while replacement is running.
- The implementation remains difficult to reason about because transform operates in this shell-expression layer.
Why this matters
Some SHB “secrets” are not just short passwords. They can be whole config-like files, for example rclone.conf, private keys, certificates, htpasswd files, or other externally generated credential files.
For these cases, SHB should either preserve the secret file contents exactly or clearly document/reject unsupported inputs. Silently changing contents, such as dropping the final newline, is surprising.
Expected behavior
Secret substitution should preserve secret contents exactly, at least for normal text secrets including trailing newlines.
If binary/NUL-containing secrets are not supported, SHB should fail clearly rather than silently corrupting the value.
Secret values should not be passed through argv.
Suggested direction
Pass secret file paths to the replacement helper and let the helper read the files directly, instead of expanding secret contents in shell and passing them as command-line arguments.
This would also make it possible to use or mirror the design of nixpkgs’ replace-secret helper, as mentioned in PR #720.
Longer term, it may be worth reconsidering transform. It makes the secret pipeline substantially harder to make safe because transforms currently operate on shell expressions rather than on explicit file inputs/outputs.
Summary
shb.replaceSecretscurrently routes secret contents through shell command substitution and then through command-line arguments. This is fragile for correctness and security.#720 improves multiline secret handling by replacing
sedwith a Python replacement helper, fixing the immediate multiline substitution failure. However, the broader design still passes secret values as CLI arguments, which means secret contents are not handled as opaque file data.Problem
Secret values are effectively read with shell command substitution, e.g. conceptually:
"$(cat "$secret_path")"and then passed as arguments to the replacement command.
This has several consequences:
Why this matters
Some SHB “secrets” are not just short passwords. They can be whole config-like files, for example
rclone.conf, private keys, certificates, htpasswd files, or other externally generated credential files.For these cases, SHB should either preserve the secret file contents exactly or clearly document/reject unsupported inputs. Silently changing contents, such as dropping the final newline, is surprising.
Expected behavior
Secret substitution should preserve secret contents exactly, at least for normal text secrets including trailing newlines.
If binary/NUL-containing secrets are not supported, SHB should fail clearly rather than silently corrupting the value.
Secret values should not be passed through argv.
Suggested direction
Pass secret file paths to the replacement helper and let the helper read the files directly, instead of expanding secret contents in shell and passing them as command-line arguments.
This would also make it possible to use or mirror the design of nixpkgs’ replace-secret helper, as mentioned in PR #720.
Longer term, it may be worth reconsidering
transform. It makes the secret pipeline substantially harder to make safe because transforms currently operate on shell expressions rather than on explicit file inputs/outputs.