{"record":{"id":"f0f7f8da7244c3b2","repo":"koala73/worldmonitor","slug":"webhook-url-dns-resolution-returned-no-addresses","errorCode":null,"errorMessage":"Webhook URL DNS resolution returned no addresses","messagePattern":"Webhook URL DNS resolution returned no addresses","errorType":"validation","errorClass":"Error","httpStatus":400,"severity":"warning","filePath":"api/_notification-webhook-ssrf.ts","lineNumber":250,"sourceCode":" * connection) because DNS can change after registration.\n */\nexport async function assertNotificationWebhookRegistrationUrlSafe(\n  rawUrl: string,\n  resolveHostname: ResolveHostname = defaultResolveHostname,\n): Promise<void> {\n  const staticError = blockedNotificationWebhookUrlReason(rawUrl);\n  if (staticError) throw new Error(staticError);\n\n  const hostname = new URL(rawUrl).hostname.toLowerCase();\n  if (isIpLiteral(hostname)) return;\n  let resolvedAddresses: string[];\n  try {\n    resolvedAddresses = await resolveHostname(hostname);\n  } catch (error) {\n    const message = error instanceof Error ? error.message : String(error);\n    throw new Error(`Webhook URL DNS resolution failed: ${message}`);\n  }\n  if (!resolvedAddresses.length) throw new Error('Webhook URL DNS resolution returned no addresses');\n  if (resolvedAddresses.some(isBlockedNotificationResolvedAddress)) {\n    throw new Error('Webhook URL must not point to a private/local address');\n  }\n}\n","sourceCodeStart":232,"sourceCodeEnd":255,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/api/_notification-webhook-ssrf.ts#L232-L255","documentation":"saveImportedFramework() rejects a framework whose name collides with any entry in loadFrameworkLibrary(), which merges BUILT_IN_FRAMEWORKS plus the imported list, so both built-in names (e.g., 'Ray Dalio Macroeconomic Cycles') and previously imported names are reserved. The comparison is exact equality on f.name. The store has no rename operation, only delete and re-add.","triggerScenarios":"Importing a framework whose name equals a built-in framework's name; re-importing a framework already in the library; double-submitting an import form so the second call sees the first save.","commonSituations":"Re-running an import that partially succeeded before; sharing framework JSON between team members who already imported it; expecting a rename that the API does not provide.","solutions":["Rename the incoming framework before importing; the error message names the conflicting name","Or delete the existing framework with that name via deleteImportedFramework(id), then import","For UI authors: pre-check availability with loadFrameworkLibrary().some(f => f.name === name) and disable submit"],"exampleFix":"// before\nsaveImportedFramework({ id, name: 'Ray Dalio Macroeconomic Cycles', description, systemPromptAppend });\n// throws: collides with the built-in framework\n\n// after\nconst taken = loadFrameworkLibrary().some(f => f.name === name);\nif (taken) {\n  showWarning(`Name '${name}' is taken. Choose another.`);\n  return;\n}\nsaveImportedFramework({ id, name, description, systemPromptAppend });","handlingStrategy":"validation","validationCode":"const taken = loadFrameworkLibrary().some(f => f.name === fw.name);\nif (taken) { suggestAlternativeName(fw.name); return; }\nsaveImportedFramework(fw);","typeGuard":null,"tryCatchPattern":"try {\n  saveImportedFramework(fw);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('already exists')) promptRename(fw);\n  else throw e;\n}","preventionTips":["Reserve built-in framework names in the import UI before submit","Disable the submit button on duplicate names with instant feedback","Remember there is no rename API: delete then re-add"],"tags":["duplicate","naming","analysis-frameworks","validation"],"backgroundTag":"duplicate-name-conflict","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}