highCVE-2026-54673Jul 24, 2026

CVE-2026-54673: builder-util-runtime Cross-Origin Redirect Credential Leak

Shubham Kandhare
Security Engagement Manager, SecureLayer7

The electron-updater HTTP redirect handler only stripped credentials from headers named exactly 'authorization' in lowercase, allowing GitLab personal access tokens and mixed-case Authorization…

Packagebuilder-util-runtime
Ecosystemnpm
Affected< 9.7.0
Fixed in9.7.0
CVE-2026-54673: builder-util-runtime Cross-Origin Redirect Credential Leak

The problem

HttpExecutor.prepareRedirectUrlOptions used a case-sensitive JavaScript property lookup (headers?.authorization) to decide whether to strip credentials before following a cross-origin redirect. JavaScript object property access is case-sensitive, so any header key that was not exactly the string 'authorization' in lowercase was never inspected.

GitLabProvider sets 'PRIVATE-TOKEN' for personal access tokens and may set 'Authorization' (capital A) for Bearer tokens. Both spellings bypass the guard entirely. When GitLab redirects an asset download to external object storage (S3, GCS), the full credential is forwarded to that destination.

An attacker who controls or can observe the redirect target receives the token.

Proof of concept

A working proof-of-concept for CVE-2026-54673 in builder-util-runtime, with the exact payload below.

http
# Trigger: configure electron-updater with a private GitLab provider
# using a personal access token (non-Bearer).
# GitLabProvider sets the header as:
#   headers["PRIVATE-TOKEN"] = "glpat-xxxxxxxxxxxxxxxxxxxx"
#
# Vulnerable prepareRedirectUrlOptions check (builder-util-runtime < 9.7.0):
#   if (headers?.authorization) {   // falsy: key is "PRIVATE-TOKEN", not "authorization"
#     if (HttpExecutor.isCrossOriginRedirect(originalUrl, parsedRedirectUrl)) {
#       delete headers.authorization
#     }
#   }
#
# Because the branch is never entered, the outgoing request to the
# cross-origin redirect destination includes:
GET /release-asset HTTP/1.1
Host: s3.amazonaws.com
PRIVATE-TOKEN: glpat-xxxxxxxxxxxxxxxxxxxx

# The same bypass applies to any mixed-case variant, e.g.:
GET /release-asset HTTP/1.1
Host: storage.googleapis.com
Authorization: Bearer ya29.XXXXXXXXXX

The root cause is CWE-202 / CWE-200: JavaScript property lookup is case-sensitive, so headers?.authorization is undefined (falsy) whenever the key is 'PRIVATE-TOKEN', 'Authorization', or any casing other than exactly 'authorization'. The delete statement is therefore never reached for those keys.

The patch (commit 22a7532bd, PR 9834) replaced the single property check with a case-insensitive, separator-agnostic loop. A normalizeName helper lowercases the key and strips '-' and '_', then tests membership against a Set called SENSITIVE_REDIRECT_HEADERS that includes 'privatetoken', 'authorization', 'xapikey', 'cookie', and several others.

This means 'PRIVATE-TOKEN', 'Private-Token', 'Authorization', and 'AUTHORIZATION' all resolve to a match and are deleted before the redirected request is sent.

The fix

Upgrade builder-util-runtime to >= 9.7.0 (shipped in electron-builder >= 26.15.0, released 2026-06-05). The patched prepareRedirectUrlOptions iterates all request headers and deletes any whose normalized name matches the SENSITIVE_REDIRECT_HEADERS set. There is no configuration-level workaround in affected versions; avoid authenticated GitLab updater flows on builder-util-runtime < 9.7.0 if upgrading is not immediately possible.

Reporter not attributed.

References: [1][2][3][4][5][6]

Related research