{"record":{"id":"cd810066ee8f5861","repo":"agalwood/Motrix","slug":"plugin-manifest-invalid-cd8100","errorCode":"PLUGIN_MANIFEST_INVALID","errorMessage":"plugin.install.registry_url_not_allowlisted","messagePattern":"plugin\\.install\\.registry_url_not_allowlisted","errorType":"error_code","errorClass":"AppError","httpStatus":null,"severity":"error","filePath":"src/core/plugin/registry/registry-fetcher.ts","lineNumber":22,"sourceCode":"// in memory BEFORE anything is written to disk.\n\nimport { createHash } from 'node:crypto'\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport { AppError, ErrorCode } from '@shared/errors'\nimport type { RegistryPluginDTO } from '@shared/schemas/registry'\n\nexport const REGISTRY_PACKAGE_HOSTS: ReadonlySet<string> = new Set([\n  'github.com',\n  'dl.motrix.app',\n])\n\nexport function assertAllowlistedPackageUrl(rawUrl: string): URL {\n  let u: URL\n  try {\n    u = new URL(rawUrl)\n  } catch {\n    throw new AppError(\n      ErrorCode.PluginManifestInvalid,\n      'plugin.install.registry_url_not_allowlisted'\n    )\n  }\n  if (u.protocol !== 'https:' || !REGISTRY_PACKAGE_HOSTS.has(u.hostname)) {\n    throw new AppError(\n      ErrorCode.PluginManifestInvalid,\n      'plugin.install.registry_url_not_allowlisted'\n    )\n  }\n  return u\n}\n\nexport async function fetchVerifiedPackageBytes(\n  entry: RegistryPluginDTO,\n  fetchImpl: typeof fetch = fetch\n): Promise<Buffer> {\n  const pkg = entry.package","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/plugin/registry/registry-fetcher.ts#L4-L40","documentation":"Thrown by assertAllowlistedPackageUrl when `new URL(rawUrl)` throws — i.e. entry.package.url is not a parseable URL string. Categorized as PLUGIN_MANIFEST_INVALID because the package URL is part of the registry DTO that describes an installable plugin. This is the first of two guards in the same function (the second, error 209, checks scheme/host).","triggerScenarios":"fetchVerifiedPackageBytes(entry) or downloadRegistryMoext(entry) is called and entry.package.url is malformed (empty, relative, missing protocol, embedded spaces, or not a string the URL constructor accepts).","commonSituations":"Registry JSON was hand-edited and the package URL was truncated. A registry entry template left a placeholder like \"<url>\". Proxy rewrote/encoded the URL badly. Schema validation upstream was bypassed.","solutions":["Inspect entry.package.url — it must be a fully-qualified URL string including scheme.","Correct the registry source so the URL is valid before install/update runs.","If consuming untrusted registry JSON, validate entry.package.url with `new URL(...)` in a try/catch before calling fetchVerifiedPackageBytes."],"exampleFix":"// before — registry entry\n{ \"package\": { \"url\": \"github.com/foo/bar/v1.moext\", \"size\": 1, \"sha256\": \"...\" } }\n\n// after\n{ \"package\": { \"url\": \"https://github.com/foo/bar/releases/download/v1/bundle.moext\", \"size\": 12345, \"sha256\": \"<64 hex>\" } }","handlingStrategy":"validation","validationCode":"import { assertAllowlistedPackageUrl } from '@core/plugin/registry/registry-fetcher'\n\nfunction isValidPackageUrl(rawUrl: string): boolean {\n  try { new URL(rawUrl); return true } catch { return false }\n}\nif (!isValidPackageUrl(entry.package?.url ?? '')) {\n  throw new Error('package.url is not a valid URL — fix the registry entry before install')\n}","typeGuard":"function isParsableUrl(s: unknown): s is string {\n  if (typeof s !== 'string') return false\n  try { new URL(s); return true } catch { return false }\n}","tryCatchPattern":"try {\n  await fetchVerifiedPackageBytes(entry)\n} catch (e) {\n  if (e instanceof AppError && e.code === ErrorCode.PluginManifestInvalid && e.message === 'plugin.install.registry_url_not_allowlisted' && !isParsableUrl(entry.package?.url)) {\n    reportMalformedRegistryUrl(entry)\n  } else throw e\n}","preventionTips":["Validate package.url with `new URL(...)` before attempting install/update.","Generate registry entries with a publisher tool that always emits fully-qualified https URLs.","Reject registry files whose package.url is missing or relative at schema-load time."],"tags":["plugin","install","registry","url","validation"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}