{"record":{"id":"f05bdbf6902cb9de","repo":"Budibase/budibase","slug":"plugin-url-must-use-https","errorCode":null,"errorMessage":"Plugin URL must use HTTPS.","messagePattern":"Plugin URL must use HTTPS\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/api/controllers/plugin/url.ts","lineNumber":16,"sourceCode":"import { downloadUnzipTarball } from \"./utils\"\nimport {\n  deleteFolderFileSystem,\n  getPluginMetadata,\n} from \"../../../utilities/fileSystem\"\n\nfunction parseTarGzUrl(url: string): URL {\n  let parsed: URL\n  try {\n    parsed = new URL(url)\n  } catch {\n    throw new Error(\"Invalid plugin URL.\")\n  }\n\n  if (parsed.protocol !== \"https:\") {\n    throw new Error(\"Plugin URL must use HTTPS.\")\n  }\n\n  if (!parsed.pathname.endsWith(\".tar.gz\")) {\n    throw new Error(\"Plugin must be compressed into a gzipped tarball.\")\n  }\n\n  return parsed\n}\n\nexport async function urlUpload(url: string, name = \"\", headers = {}) {\n  parseTarGzUrl(url)\n\n  const path = await downloadUnzipTarball(url, name, headers, {\n    followRedirects: false,\n  })\n  try {\n    return await getPluginMetadata(path)\n  } catch (err) {","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/plugin/url.ts#L1-L34","documentation":"Thrown when the plugin tarball URL parses but does not use the https: protocol. Budibase requires HTTPS for remote plugin downloads so code is not fetched over plaintext.","triggerScenarios":"urlUpload called with an http:// URL for a .tar.gz plugin archive.","commonSituations":"Self-hosted file servers still on plain HTTP; old documentation links using http; internal network hosts without TLS.","solutions":["Serve the tarball over HTTPS (add TLS via a reverse proxy or Let's Encrypt)","Use an HTTPS-capable host (e.g. GitHub releases, S3 with TLS)","If purely internal, front the file server with an HTTPS proxy"],"exampleFix":"// before\nawait urlUpload('http://files.example.com/plugin.tar.gz')\n// after\nawait urlUpload('https://files.example.com/plugin.tar.gz')","handlingStrategy":"validation","validationCode":"const u = new URL(url)\nif (u.protocol !== 'https:') throw new Error('Plugin URL must start with https://')","typeGuard":"function isHttpsUrl(url: string): boolean {\n  try { return new URL(url).protocol === 'https:' } catch { return false }\n}","tryCatchPattern":"try {\n  await urlUpload(url)\n} catch (err) {\n  if (err.message === 'Plugin URL must use HTTPS.') {\n    // switch the host to an HTTPS endpoint\n  }\n}","preventionTips":["Serve plugin tarballs behind TLS (reverse proxy + Let's Encrypt)","Audit stored plugin URLs for http:// and migrate them","Default to HTTPS links from GitHub releases/S3"],"tags":["security","https","plugins","url"],"backgroundTag":"insecure-http-url","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}