iOfficeAI/AionUi · error · Error

update.errors.hostNotAllowed

update.errors.hostNotAllowed

Error message

update.errors.hostNotAllowed

What it means

Thrown by assertAllowedUrl when the URL's hostname is not in ALLOWED_DOWNLOAD_HOSTS. The updater uses a strict allowlist so downloads/redirects can only hit known-good hosts (e.g. GitHub, the project CDN), blocking SSRF-style exfiltration through the update channel.

Source

Thrown at packages/desktop/src/process/bridge/updateBridge.ts:282

const resolveRepo = (requestRepo?: string): string => {
  const envRepo = process.env.AIONUI_GITHUB_REPO?.trim();
  const repo = (requestRepo || envRepo || DEFAULT_REPO).trim();
  return repo || DEFAULT_REPO;
};

const assertAllowedUrl = async (rawUrl: string) => {
  let parsed: URL;
  try {
    parsed = new URL(rawUrl);
  } catch {
    throw new Error((await getI18n()).t('update.errors.invalidUrl'));
  }

  if (parsed.protocol !== 'https:') {
    throw new Error((await getI18n()).t('update.errors.httpsOnly'));
  }
  if (!ALLOWED_DOWNLOAD_HOSTS.has(parsed.hostname)) {
    throw new Error((await getI18n()).t('update.errors.hostNotAllowed', { host: parsed.hostname }));
  }
};

const fetchWithAllowlistedRedirects = async (rawUrl: string, signal: AbortSignal): Promise<Response> => {
  let current = rawUrl;

  for (let i = 0; i <= MAX_REDIRECTS; i++) {
    await assertAllowedUrl(current);

    const res = await fetch(current, {
      signal,
      redirect: 'manual',
      headers: {
        'User-Agent': DEFAULT_USER_AGENT,
      },
    });

    if (res.status >= 300 && res.status < 400) {

View on GitHub (pinned to 711aa0550e)

Solutions

  1. Use an allowlisted host (check the ALLOWED_DOWNLOAD_HOSTS constant in updateBridge.ts for the accepted set)
  2. If you self-host, add your host to ALLOWED_DOWNLOAD_HOSTS in a fork/local build
  3. Verify you are not accidentally redirecting to an off-list CDN
  4. Confirm DNS/mirror config serves from the expected allowlisted domain

Example fix

// before
const ALLOWED_DOWNLOAD_HOSTS = new Set(['github.com']);

// after (self-hosted setup)
const ALLOWED_DOWNLOAD_HOSTS = new Set([
  'github.com',
  'releases.mycompany.com',
]);
Defensive patterns

Strategy: validation

Validate before calling

import { ALLOWED_DOWNLOAD_HOSTS } from './updateBridge';
const isAllowedHost = (u: string): boolean => {
  try { return ALLOWED_DOWNLOAD_HOSTS.has(new URL(u).hostname); } catch { return false; }
};
if (!isAllowedHost(downloadUrl)) throw new Error('host not in update allowlist');
await fetchWithAllowlistedRedirects(downloadUrl, signal);

Type guard

const isAllowlistedHost = (u: string): u is string =>
  (() => { try { return ALLOWED_DOWNLOAD_HOSTS.has(new URL(u).hostname); } catch { return false; } })();

Try / catch

try {
  await fetchWithAllowlistedRedirects(url, signal);
} catch (err) {
  if (err instanceof Error && err.message.includes('hostNotAllowed')) {
    // surface to user: this mirror/host is unsupported; fall back to GitHub releases
  } else throw err;
}

Prevention

When it happens

Trigger: Passing an update URL whose host is not on the allowlist — e.g. a self-hosted release server, a GitHub Enterprise host, or a proxy domain. The failing hostname is interpolated into the message via the host variable.

Common situations: Self-hosting releases behind a custom domain; corporate mirrors; pointing the updater at a staging host that was never allowlisted; the allowlist constant edited/removed during refactoring; redirects that land on a CDN host outside the list.

Related errors


AI-assisted analysis of iOfficeAI/AionUi@711aa0550e (2026-08-28). Data as JSON: /api/errors/74c8c475e4b2e2dc. Report an issue: GitHub.