Leaking passwords by trying hard not to leak them

TL;DR: The credentials we use to talk to ci.centos.org briefly leaked yesterday (2018-12-06) on ci.theforeman.org. The password has been changed and we have no reason to believe any harm has been done with the leaked password.

Long version:
Some time ago, we wanted our jobs to use Ansible’s extra vars as a file instead of passing them on the command line as this allows to store more complex data structures and eliminates the need to shell-escape special characters. We also wanted to archive the generated extra vars files as artifacts of the job for easier debugging. As we knew that we pass sensitive data (the password for ci.centos.org, to trigger jobs there) as extra vars, we added a flag to the archiving code to not archive that sensitive data:

def writeExtraVars(args) {
    def timestamp = new Date().getTime()
    def extra_vars_file = 'extra_vars-' + timestamp.toString() + '.yaml'
    def extra_vars = args.extraVars ?: [:]
    def archive_extra_vars = args.archiveExtraVars ?: true
    writeYaml file: extra_vars_file, data: extra_vars
    if (archive_extra_vars) {
        archiveArtifacts artifacts: extra_vars_file
    return extra_vars_file

The assumption was that when we call writeExtraVars(..., archiveExtraVars: false) the archiveArtifacts would be skipped, and we’d be safe.

However, def archive_extra_vars = args.archiveExtraVars ?: true resolves to true in both cases, when args.archiveExtraVars is null (that’s what we wanted) and false (that’s what we not wanted).

The bug was fixed by explicitly checking if args.archiveExtraVars is null and only then defaulting to true.

Sorry for the inconvenience and the fact that yesterdays builds are missing from Jenkins as they’ve been deleted.