{"record":{"id":"72e2ad43f5001645","repo":"abhigyanpatwari/GitNexus","slug":"allow-insecure-connection-gitnexus-allow-insec","errorCode":null,"errorMessage":"--allow-insecure-connection / GITNEXUS_ALLOW_INSECURE_CONNECTION entries must be exact hostnames or IP addresses","messagePattern":"--allow-insecure-connection / GITNEXUS_ALLOW_INSECURE_CONNECTION entries must be exact hostnames or IP addresses","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/wiki/llm-client.ts","lineNumber":188,"sourceCode":"function formatTimeoutDuration(timeoutMs: number): string {\n  if (timeoutMs >= 1000 && timeoutMs % 1000 === 0) {\n    return `${timeoutMs / 1000}s`;\n  }\n  return `${timeoutMs}ms`;\n}\n\nfunction isTimeoutLikeError(err: unknown): boolean {\n  if (!(err instanceof Error)) return false;\n  if (err.name === 'TimeoutError' || err.name === 'AbortError') return true;\n  return /time(d)?\\s*out|timeout/i.test(err.message);\n}\n\nexport const LLM_ALLOW_INSECURE_CONNECTION_ENV = 'GITNEXUS_ALLOW_INSECURE_CONNECTION';\n\nfunction normalizeAllowedInsecureHttpHost(host: string): string {\n  const trimmed = host.trim().toLowerCase();\n  const fail = () => {\n    throw new Error(\n      `--allow-insecure-connection / ${LLM_ALLOW_INSECURE_CONNECTION_ENV} entries must be exact hostnames or IP addresses`,\n    );\n  };\n  if (!trimmed || /[/@?#]/.test(trimmed)) fail();\n\n  if (trimmed.startsWith('[')) {\n    if (!trimmed.endsWith(']')) fail();\n    const normalized = trimmed.slice(1, -1);\n    if (!normalized || /[\\[\\]]/.test(normalized)) fail();\n    return normalized;\n  }\n\n  if (/[\\[\\]]/.test(trimmed)) fail();\n  if ((trimmed.match(/:/g)?.length ?? 0) === 1) {\n    // URL.hostname never includes the port, so accepting \"host:port\" would\n    // create a confusing no-op allowlist entry.\n    fail();\n  }","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/core/wiki/llm-client.ts#L170-L206","documentation":"Entries passed via `--allow-insecure-connection <host>` or GITNEXUS_ALLOW_INSECURE_CONNECTION must be bare hostnames or IP addresses so they can be compared against URL.hostname. normalizeAllowedInsecureHttpHost rejects empty entries, anything containing '/', '@', '?', or '#' (schemes, paths, userinfo), malformed bracketed IPv6 like '[::1' or 'a[b]c', and 'host:port' values (URL.hostname never contains the port, so a port would create a silent no-op entry). The check runs while parsing the comma-separated list, before any request is made.","triggerScenarios":"Passing a full URL fragment such as `--allow-insecure-connection http://192.168.1.10:8080` or `llm.lan/v1`; adding a port (`myhost:11434`); an empty item from a trailing/doubled comma in GITNEXUS_ALLOW_INSECURE_CONNECTION; a half-typed IPv6 literal `[::1`. Note bare multi-colon IPv6 like `::1` is accepted.","commonSituations":"Copy-pasting the base URL into the allowlist flag instead of just its host; users assuming the flag takes a URL; environment variables assembled by scripts that leave empty fields; trying to scope the exception to a port (unsupported — the exception is per host).","solutions":["Pass only the hostname/IP: `--allow-insecure-connection 192.168.1.10` (no scheme, path, or port)","For IPv6 use the bare or bracketed literal: `--allow-insecure-connection '[::1]'` or `::1`","Check the env var value for trailing commas/empty segments: GITNEXUS_ALLOW_INSECURE_CONNECTION=host1,host2","Prefer switching the endpoint to https:// so no allowlist entry is needed at all"],"exampleFix":"# before\ngitnexus wiki --provider custom --base-url http://192.168.1.10:8080/v1 --allow-insecure-connection http://192.168.1.10:8080\n\n# after\ngitnexus wiki --provider custom --base-url http://192.168.1.10:8080/v1 --allow-insecure-connection 192.168.1.10","handlingStrategy":"validation","validationCode":"const HOST_RE = /^[a-z0-9._-]+$/i; // or IPv6 literal\nfunction isValidInsecureHost(entry: string): boolean {\n  const t = entry.trim().toLowerCase();\n  if (!t || /[/@?#]/.test(t)) return false;\n  if (t.startsWith('[')) return /^\\[[0-9a-f:]+\\]$/i.test(t);\n  if (/[\\[\\]]/.test(t)) return false;\n  return (t.match(/:/g)?.length ?? 0) !== 1; // reject host:port\n}\nconst hosts = raw.split(',').filter(isValidInsecureHost); // validate before passing","typeGuard":"function isValidInsecureHost(entry: string): boolean {\n  const t = entry.trim().toLowerCase();\n  if (!t || /[/@?#]/.test(t)) return false;\n  if (t.startsWith('[')) return /^\\[[0-9a-f:]+\\]$/i.test(t);\n  if (/[\\[\\]]/.test(t)) return false;\n  return (t.match(/:/g)?.length ?? 0) !== 1;\n}","tryCatchPattern":"try {\n  parseLLMAllowedInsecureHttpHosts(envValue);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('exact hostnames or IP addresses')) {\n    // strip scheme/port from each entry and retry: new URL(entry).hostname\n  }\n}","preventionTips":["Derive the allowlist from the base URL host (new URL(baseUrl).hostname) instead of typing it manually","Never include scheme, path, userinfo, or port in allowlist entries — hostname only","Lint env vars that assemble comma-separated host lists for empty segments"],"tags":["llm","configuration","ssrf-protection","http","gitnexus"],"backgroundTag":"invalid-config-value","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}