{"record":{"id":"450f2579e0abc9a4","repo":"BigPizzaV3/CodexPlusPlus","slug":"api-https","errorCode":null,"errorMessage":"API 地址须使用 HTTPS，且不含账号、密码、查询参数或片段","messagePattern":"API 地址须使用 HTTPS，且不含账号、密码、查询参数或片段","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"tools/conversation-canvas/public/canvas.user.js","lineNumber":623,"sourceCode":"  for(;;){\n    signal?.throwIfAborted();\n    const conversation=manager.getConversation(session.sideId);\n    if(!conversation)throw Error('后台整理会话已失效，已完成批次仍保留；点击继续可重新处理本批');\n    const turn=nativeTurns(conversation).find(t=>t.turnId===session.turnId);\n    if(turn&&turn.status!=='inProgress'){\n      if(turn.status!=='completed'||turn.error){session.requestId=null;session.turnId=null;session.phase='ready';await save();throw Error('本批整理中断，已完成批次已保存，可点击继续');}\n      if(session.phase!=='completed')session.batches=(session.batches||0)+1;\n      session.phase='completed';await save();\n      return turn.items.filter(i=>i.type==='agentMessage'&&(i.phase==null||i.phase==='final_answer')).map(i=>i.text??'').join('\\n');\n    }\n    await new Promise(resolve=>setTimeout(resolve,1000));\n  }\n}\n\n  // OpenAI-compatible Chat Completions. Never persist credentials in tree checkpoints.\nfunction apiEndpoint(raw){\n  let url;try{url=new URL(String(raw).trim());}catch{throw Error('请输入完整的 HTTPS API 地址');}\n  if(url.protocol!=='https:'||url.username||url.password||url.search||url.hash)throw Error('API 地址须使用 HTTPS，且不含账号、密码、查询参数或片段');\n  const path=url.pathname.replace(/\\/+$/,'');\n  url.pathname=path.endsWith('/chat/completions')?path:(path||'/v1')+'/chat/completions';\n  return url.href;\n}\n\nfunction apiConfig(input){\n  const channel=input?.channel==='external'?'external':'native';\n  const value={channel,baseUrl:String(input?.baseUrl||'').trim(),model:String(input?.model||'').trim(),key:String(input?.key||'').trim(),remember:input?.remember===true,speed:input?.speed==='provider'?'provider':'fast',revision:input?.revision||crypto.randomUUID()};\n  if(channel==='external'){\n    value.endpoint=apiEndpoint(value.baseUrl);\n    if(!value.model||value.model.length>200)throw Error('请填写 API 的模型名称');\n    if(!value.key||/[\\r\\n]/.test(value.key))throw Error('请在设置中填写有效 API Key');\n  }\n  return value;\n}\n\nfunction storedApiConfig(config){\n  const {channel,baseUrl,model,remember,speed,revision}=config;","sourceCodeStart":605,"sourceCodeEnd":641,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/tools/conversation-canvas/public/canvas.user.js#L605-L641","documentation":"After parsing, apiEndpoint() enforces URL hygiene: protocol must be https:, with no embedded username/password, query string, or fragment. This prevents credentials leaking in the URL and avoids ambiguity in the endpoint. Violations throw this error.","triggerScenarios":"baseUrl uses http:// (not https), contains user:pass@, or carries ?query or #fragment — e.g. pasting a URL that already includes ?api-key=... or a #section anchor.","commonSituations":"User pasted a URL with an API key query parameter (a security anti-pattern the check blocks); used a local http:// dev endpoint; included an anchor copied from docs.","solutions":["Use https:// and remove any username, password, ?query, and #fragment from the base URL.","Pass the API key in the dedicated key field, never in the URL.","For local testing with plain http, use an HTTPS tunnel/proxy (the check intentionally rejects http)."],"exampleFix":"// before\nbaseUrl = 'http://api.example.com/v1?key=abc';\n// after\nbaseUrl = 'https://api.example.com/v1'; // key entered in the API Key field","handlingStrategy":"validation","validationCode":"function cleanUrl(raw){ const u = new URL(String(raw).trim()); return u.protocol === 'https:' && !u.username && !u.password && !u.search && !u.hash; }","typeGuard":"const isCleanHttpsUrl = (s) => { try { const u = new URL(s); return u.protocol === 'https:' && !u.username && !u.password && !u.search && !u.hash; } catch { return false; } };","tryCatchPattern":"try { endpoint = apiEndpoint(raw); } catch (e) { if (e.message.includes('HTTPS')) instructUserToUseKeyField(); }","preventionTips":["Never embed credentials or API keys in the URL; use the dedicated key field.","Strip query strings and fragments before saving provider base URLs.","Prefer providers' HTTPS endpoints; tunnel local http services through HTTPS for testing."],"tags":["url","security","https","validation"],"backgroundTag":"invalid-url-format","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}