Files
hermes-agent/apps/desktop/update-feed.cjs
ethernet b8c64260d8 desktop: validate the feed base baked into app-update.yml
electron-builder.config.cjs copied CLOUDFLARE_R2_PUBLIC_URL into the
generic publish provider after only trimming a trailing slash. That URL
ends up in app-update.yml, which every installed client trusts for the
life of the build, so an http://, credentialed, or query-bearing var
would have shipped silently.

update-feed.cjs (the feed contract) now owns feedBaseUrl(): https only,
no credentials/query/fragment, spelled as the URL parser re-serializes
it (trailing slash tolerated, like the Python side). Both the Windows
and macOS publish entries read the validated value.
2026-09-21 18:47:07 -04:00

41 lines
1.6 KiB
JavaScript

'use strict'
// Historical native URLs are a compatibility layout, not a channel registry.
/** @param {string} channel @param {boolean} light */
function darwinFeed(channel, light = false) {
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(channel) || channel.length > 32 ||
/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/.test(channel)) {
throw new TypeError('Invalid channel name')
}
return {
directory: `releases/darwin/${light ? 'light/' : ''}${channel}`,
channel,
fileName: `${channel}-mac.yml`,
allowPrerelease: channel !== 'stable'
}
}
// The feed origin is baked into app-update.yml and trusted by every
// installed client for the life of the build, so the CI var must be a
// canonical https base: no credentials, query, or fragment, spelled exactly
// as the URL parser re-serializes it (a trailing slash is tolerated, as the
// Python release scripts do). Anything else is a misconfigured release
// environment and must fail the build, not ship.
/** @param {string | undefined} raw @returns {string | undefined} */
function feedBaseUrl(raw) {
if (raw === undefined || raw === '') return undefined
let url
try {
url = new URL(raw)
} catch {
throw new TypeError(`CLOUDFLARE_R2_PUBLIC_URL is not a URL: ${raw}`)
}
const canonical = url.origin + url.pathname.replace(/\/+$/, '')
if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash || canonical !== raw.replace(/\/+$/, '')) {
throw new TypeError(`CLOUDFLARE_R2_PUBLIC_URL must be a canonical https base (got ${raw}, expected ${canonical})`)
}
return canonical
}
module.exports = { darwinFeed, feedBaseUrl }