From b0df6dc10eb5b9e9fdca93063a16316f8589954e Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 20 Jun 2026 19:50:09 +0200 Subject: [PATCH] fix(package): scope trusted URL auth to original origin --- .../resolve-openclaw-package-candidate.mjs | 8 ++- ...resolve-openclaw-package-candidate.test.ts | 50 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/scripts/resolve-openclaw-package-candidate.mjs b/scripts/resolve-openclaw-package-candidate.mjs index c83bf0e2db52..30d74c4bd67a 100644 --- a/scripts/resolve-openclaw-package-candidate.mjs +++ b/scripts/resolve-openclaw-package-candidate.mjs @@ -963,10 +963,13 @@ function validateTrustedPackageDownloadUrl(parsed, trustedSource, options = {}) } } -function createTrustedPackageAuthHeaders(trustedSource) { +function createTrustedPackageAuthHeaders(trustedSource, parsed, initialOrigin) { if (!trustedSource?.auth) { return undefined; } + if (parsed.origin !== initialOrigin) { + return undefined; + } const token = process.env[TRUSTED_PACKAGE_SOURCE_TOKEN_ENV]; if (!token) { throw new Error( @@ -1193,8 +1196,8 @@ async function openPackageDownloadResponse(url, options) { const timeoutMs = options.timeoutMs ?? PACKAGE_URL_DOWNLOAD_TIMEOUT_MS; const maxRedirects = options.maxRedirects ?? PACKAGE_URL_MAX_REDIRECTS; const trustedSource = options.trustedSource; - const headers = createTrustedPackageAuthHeaders(trustedSource); let parsed = new URL(url); + const initialOrigin = parsed.origin; for (let redirectCount = 0; redirectCount <= maxRedirects; redirectCount += 1) { if (trustedSource) { validateTrustedPackageDownloadUrl(parsed, trustedSource, { isRedirect: redirectCount > 0 }); @@ -1202,6 +1205,7 @@ async function openPackageDownloadResponse(url, options) { validatePackageDownloadUrl(parsed); } const addresses = await resolvePackageDownloadAddresses(parsed, lookupHost, trustedSource); + const headers = createTrustedPackageAuthHeaders(trustedSource, parsed, initialOrigin); const opened = options.fetchImpl ? await openFetchPackageDownloadResponse(parsed, { fetchImpl: options.fetchImpl, diff --git a/test/scripts/resolve-openclaw-package-candidate.test.ts b/test/scripts/resolve-openclaw-package-candidate.test.ts index 906223ad9f00..a5bef0d2893b 100644 --- a/test/scripts/resolve-openclaw-package-candidate.test.ts +++ b/test/scripts/resolve-openclaw-package-candidate.test.ts @@ -669,6 +669,56 @@ describe("resolve-openclaw-package-candidate", () => { ).rejects.toThrow("is not allowed by trusted package source enterprise-artifactory"); }); + it("does not forward trusted package auth headers to redirect hosts", async () => { + const dir = await mkdtemp(path.join(tmpdir(), "openclaw-package-download-")); + tempDirs.push(dir); + const target = path.join(dir, "openclaw.tgz"); + const previousToken = process.env.OPENCLAW_TRUSTED_PACKAGE_TOKEN; + process.env.OPENCLAW_TRUSTED_PACKAGE_TOKEN = "token-123"; + const trustedSource = { + allowPrivateNetwork: true, + auth: { type: "bearer" }, + hosts: ["packages.internal"], + id: "enterprise-artifactory", + pathPrefixes: ["/artifactory/openclaw/"], + ports: [8443], + redirectHosts: ["packages.internal", "mirror.internal"], + }; + const requestHeaders: Array | undefined> = []; + + try { + await downloadUrl("https://packages.internal:8443/artifactory/openclaw/openclaw.tgz", target, { + fetchImpl: async (_url: URL, init?: RequestInit) => { + requestHeaders.push(init?.headers as Record | undefined); + if (requestHeaders.length === 1) { + return new Response(null, { + headers: { + location: "https://mirror.internal:8443/artifactory/openclaw/openclaw.tgz", + }, + status: 302, + }); + } + return new Response(new Uint8Array([4, 5, 6]), { + headers: { "content-length": "3" }, + status: 200, + }); + }, + lookupHost: lookupAddresses([{ address: "10.0.0.8", family: 4 }]), + maxBytes: 3, + trustedSource, + }); + } finally { + if (previousToken === undefined) { + delete process.env.OPENCLAW_TRUSTED_PACKAGE_TOKEN; + } else { + process.env.OPENCLAW_TRUSTED_PACKAGE_TOKEN = previousToken; + } + } + + expect(requestHeaders).toEqual([{ authorization: "Bearer token-123" }, undefined]); + await expect(readFile(target)).resolves.toEqual(Buffer.from([4, 5, 6])); + }); + it("validates redirects for package_url downloads", async () => { const dir = await mkdtemp(path.join(tmpdir(), "openclaw-package-download-")); tempDirs.push(dir);