GPG passphrase leaks to build logs via `set -x` in scripts emitted by `Decrypt.commands()`
#2,325 opened on May 11, 2026
Repository metrics
- Stars
- (575 stars)
- PR merge metrics
- (PR metrics pending)
Description
What happens
In src/main/java/com/rultor/agents/req/Decrypt.java, the GPG decryption command is built by interpolating the symmetric passphrase directly into a shell command line:
final String script = String.format(
"gpg --batch --decrypt --passphrase %s ...",
Ssh.escape(String.format("rultor-key:%s", this.profile.name()))
);
The passphrase rultor-key:<profile-name> is the actual symmetric secret used to decrypt the repository's .enc files. It is embedded into the run.sh script that StartsDaemon uploads to the build host, where run.sh runs under set -ex (StartsDaemon writes set -ex -o pipefail at the top of every script). With -x, bash prints every command — including this one with the passphrase — to stdout. That stdout is captured into the rultor build log, which is published.
So a single rultor build of a public project writes the decryption passphrase in plaintext into a log that is publicly readable. Any reader who also obtains the .enc files can decrypt the repository's secrets.
Compounding the leak: the passphrase is a deterministic function of the profile name (rultor-key:<owner>/<repo>), so an attacker who scrapes a public build log for one project does not even need to do additional work to derive future passphrases for that same project, and can guess passphrases for any other project whose name they know.
What should happen
Two changes, either of which closes the leak, both together being best:
-
Pass the passphrase via
--passphrase-file /dev/stdin(or--passphrase-fd 0) and pipe it in, so it never appears inset -xoutput. As a minimum:set +x printf '%s' "$passphrase" | gpg --batch --decrypt --passphrase-fd 0 ... set -x -
Derive the passphrase from a per-installation server-side secret (rultor's own configuration) combined with the profile name, rather than from the profile name alone. That way leaking one log only compromises one project, not the whole rultor installation's threat model.
Result: the symmetric passphrase no longer appears in any build log, and one accidental log leak does not enable secret extraction for arbitrary repositories.