{"record":{"id":"e758b4feadf6a8de","repo":"toeverything/AFFiNE","slug":"caldav-insecure-url","errorCode":"caldav_insecure_url","errorMessage":"CalDAV URL must use https.","messagePattern":"CalDAV URL must use https\\.","errorType":"exception","errorClass":"GraphqlBadRequest","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/plugins/calendar/providers/caldav.ts","lineNumber":573,"sourceCode":"    }\n  }\n\n  private async assertAllowedUrl(urlValue: string) {\n    let url: URL;\n    try {\n      url = new URL(urlValue);\n    } catch {\n      throw new GraphqlBadRequest({\n        code: 'caldav_invalid_url',\n        message: 'CalDAV URL is invalid.',\n      });\n    }\n\n    if (\n      url.protocol !== 'https:' &&\n      !(url.protocol === 'http:' && this.allowInsecureHttp)\n    ) {\n      throw new GraphqlBadRequest({\n        code: 'caldav_insecure_url',\n        message: 'CalDAV URL must use https.',\n      });\n    }\n\n    const hostname = url.hostname.toLowerCase();\n    if (\n      this.allowedHosts.length &&\n      !isAllowedHost(hostname, this.allowedHosts)\n    ) {\n      throw new GraphqlBadRequest({\n        code: 'caldav_host_blocked',\n        message: 'CalDAV host is not allowed.',\n      });\n    }\n  }\n\n  private toGraphqlSsrfError(error: unknown) {","sourceCodeStart":555,"sourceCodeEnd":591,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/plugins/calendar/providers/caldav.ts#L555-L591","documentation":"After URL parsing succeeds, the CalDAV provider enforces transport security: any protocol other than https: throws GraphqlBadRequest with code caldav_insecure_url, unless the deployment explicitly enables allowInsecureHttp and the URL is plain http. This keeps calendar credentials and data off plaintext transports.","triggerScenarios":"Submitting a http:// CalDAV URL on a server where the CalDAV allowInsecureHttp option is false (the default), or a non-https/non-http scheme such as ftp://.","commonSituations":"Self-hosted CalDAV servers without TLS; local development instances; typo of http:// where https:// was intended; enabling http in dev but forgetting the production flag difference.","solutions":["Use an https:// CalDAV endpoint — put the server behind TLS.","For local development only, enable the calendar CalDAV allowInsecureHttp option in server config; never enable it in production.","Check the URL for a missing 's' in the scheme."],"exampleFix":"// before\nawait caldav.connect('http://caldav.local/user'); // blocked when allowInsecureHttp is false\n\n// after\nawait caldav.connect('https://caldav.example.com/user');\n// or, dev-only: set calendar.caldav.allowInsecureHttp = true in server config","handlingStrategy":"validation","validationCode":"const allowInsecure = config.calendar.caldav.allowInsecureHttp === true;\nfunction isAllowedScheme(url: string): boolean {\n  const proto = new URL(url).protocol;\n  return proto === 'https:' || (proto === 'http:' && allowInsecure);\n}\nif (!isAllowedScheme(caldavUrl)) {\n  // prompt for an https:// URL before calling the API\n}","typeGuard":"function isHttpsUrl(v: string): v is `https://${string}` {\n  try { return new URL(v).protocol === 'https:'; } catch { return false; }\n}","tryCatchPattern":"try { await caldav.connect(url); } catch (e) { if (e.code === 'caldav_insecure_url') suggestHttpsUrl(); else throw e; }","preventionTips":["Default all CalDAV endpoints to https in UI examples and placeholders.","Enable allowInsecureHttp only in local dev, never in production config.","Reject non-http(s) schemes outright in client validation."],"tags":["calendar","caldav","https","security","configuration"],"backgroundTag":"https-required","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}