The secret was masked until we base64 encoded it
An engineer scrolling a failed build on a Monday afternoon found our registry deploy token sitting in the log in plain sight. Not the whole log. One line, inside a curl command, printed by a step that had been running unchanged for fourteen months. Masking in CI is string replacement. The runner holds a list of registered secret values and rewrites any exact match before a line reaches the log.…
A developer discovered a registry deploy token in plain text in the build log during a routine Monday afternoon check. The token, which should have been securely masked, was mistakenly included in a curl command output for fourteen months. The CI system employs string replacement to mask secrets, but in this case, the runner echoed every command it executed with 'set -x', revealing the token alongside the username.
The runner was unaware of the relationship between the two values, and no direct mention of the token existed in the log. The same mistake was made twice in the same log file. When the registry rejected a push, it included the request headers in the error body logged by the system. Consequently, anyone with repository read access, which in this organization is everyone, could view the token for four hundred days.
To prevent future incidents, several actions were taken. Secrets derived from a secret value must be explicitly registered with the runner before use, and steps handling credentials should not run with shell tracing enabled. The workflow files now include a lint rule to enforce these guidelines. Additionally, log responses from authenticated calls are now logged by status code and a whitelist of fields instead of the entire response object.
The long-lived token has been revoked, and the deploy job now uses a short-lived credential obtained through OIDC. Lastly, the company conducts regular scans of recent logs for known key prefixes and high entropy strings, which helped identify an additional leaked token in another repository belonging to an inactive service. Masking protects the exact string provided, but a pipeline transforms that string into various other strings throughout its lifecycle.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.