{"record":{"id":"fe03fb11e4752b3a","repo":"iOfficeAI/AionUi","slug":"update-errors-httpsonly","errorCode":"update.errors.httpsOnly","errorMessage":"update.errors.httpsOnly","messagePattern":"update\\.errors\\.httpsOnly","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/desktop/src/process/bridge/updateBridge.ts","lineNumber":279,"sourceCode":"  };\n};\n\nconst resolveRepo = (requestRepo?: string): string => {\n  const envRepo = process.env.AIONUI_GITHUB_REPO?.trim();\n  const repo = (requestRepo || envRepo || DEFAULT_REPO).trim();\n  return repo || DEFAULT_REPO;\n};\n\nconst assertAllowedUrl = async (rawUrl: string) => {\n  let parsed: URL;\n  try {\n    parsed = new URL(rawUrl);\n  } catch {\n    throw new Error((await getI18n()).t('update.errors.invalidUrl'));\n  }\n\n  if (parsed.protocol !== 'https:') {\n    throw new Error((await getI18n()).t('update.errors.httpsOnly'));\n  }\n  if (!ALLOWED_DOWNLOAD_HOSTS.has(parsed.hostname)) {\n    throw new Error((await getI18n()).t('update.errors.hostNotAllowed', { host: parsed.hostname }));\n  }\n};\n\nconst fetchWithAllowlistedRedirects = async (rawUrl: string, signal: AbortSignal): Promise<Response> => {\n  let current = rawUrl;\n\n  for (let i = 0; i <= MAX_REDIRECTS; i++) {\n    await assertAllowedUrl(current);\n\n    const res = await fetch(current, {\n      signal,\n      redirect: 'manual',\n      headers: {\n        'User-Agent': DEFAULT_USER_AGENT,\n      },","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/iOfficeAI/AionUi/blob/711aa0550ee183ea495dc33e2c05c7943b70a60a/packages/desktop/src/process/bridge/updateBridge.ts#L261-L297","documentation":"Thrown by assertAllowedUrl when the URL parses successfully but its protocol is anything other than https:. The updater intentionally enforces HTTPS-only downloads to prevent MITM attacks on update artifacts.","triggerScenarios":"Passing an http:// URL to any update fetch path guarded by assertAllowedUrl. Also fires for file://, ftp://, or other schemes — only exactly 'https:' is accepted.","commonSituations":"Local development against a test update server over http://; an old config created before HTTPS was enforced; a company-internal mirror that only serves http; copy-pasting a URL from docs that used http.","solutions":["Switch the URL to https:// (install a cert / use HTTPS endpoint on the update server)","For local testing, use a self-signed cert with NODE_EXTRA_CA_CERTS rather than downgrading to http","Update the stored feed URL to its HTTPS equivalent","If you control the server, enable TLS (e.g. via a reverse proxy with Let's Encrypt)"],"exampleFix":"// before\nawait assertAllowedUrl('http://updates.example.com/app.json');\n\n// after\nawait assertAllowedUrl('https://updates.example.com/app.json');","handlingStrategy":"validation","validationCode":"if (!/^https:\\/\\//i.test(feedUrl.trim())) {\n  throw new Error('update URL must start with https://');\n}\nawait initUpdateBridge(feedUrl.trim());","typeGuard":"const isHttpsUrl = (s: string): boolean => {\n  try { return new URL(s).protocol === 'https:'; } catch { return false; }\n};","tryCatchPattern":"try {\n  await assertAllowedUrl(url);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('httpsOnly')) {\n    url = url.replace(/^http:/, 'https:'); // then retry\n  } else throw err;\n}","preventionTips":["Enforce https in the settings UI before save","Use https endpoints in all environments including staging","Never downgrade to http for local tests; use dev certs instead"],"tags":["security","updater","url-validation","https"],"backgroundTag":"https-only-transport-required","analyzedSha":"711aa0550ee183ea495dc33e2c05c7943b70a60a","analyzedAt":"2026-08-28T07:56:06.558Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}