fix(package): scope trusted URL auth to original origin

This commit is contained in:
Vincent Koc
2026-06-20 19:50:09 +02:00
parent 141fb2b119
commit b0df6dc10e
2 changed files with 56 additions and 2 deletions
@@ -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,
@@ -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<Record<string, string> | 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<string, string> | 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);