From 541e4e070cdcd2355ade180714c453f90f4831a6 Mon Sep 17 00:00:00 2001 From: Aleksandr Lesnenko Date: Wed, 8 Jul 2026 15:07:52 -0400 Subject: [PATCH] trust the OS certificate store for TLS connections --- src/cli.ts | 2 ++ src/core/http/network-error.ts | 3 ++- src/core/http/system-ca.ts | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/core/http/system-ca.ts diff --git a/src/cli.ts b/src/cli.ts index 2443905..1056c2c 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -4,6 +4,7 @@ import type { ArgsDef, CommandDef } from "citty"; import { hoistGlobalFlags } from "./commands/global-flags"; import { ConfigError } from "./core/errors"; +import { trustSystemCa } from "./core/http/system-ca"; import main from "./main"; import { reportError } from "./output/error"; import { findUnknownCommand, resolveBreadcrumb, showUsage, showUsageJson } from "./output/help"; @@ -12,6 +13,7 @@ const HELP_FLAGS: ReadonlySet = new Set(["--help", "-h"]); const JSON_HELP_FLAG = "--json"; async function run(): Promise { + trustSystemCa(); const rawArgs = hoistGlobalFlags(process.argv.slice(2)); const wantsJsonHelp = rawArgs.includes(JSON_HELP_FLAG); diff --git a/src/core/http/network-error.ts b/src/core/http/network-error.ts index 1441e88..1e00636 100644 --- a/src/core/http/network-error.ts +++ b/src/core/http/network-error.ts @@ -45,7 +45,8 @@ function networkMessage(code: string | null, target: NetworkTarget, fallback: st if (TLS_ERROR_CODES.has(code)) { return ( `Could not reach Metabase: TLS error contacting ${target.host} (${code}) — ` + - `the certificate could not be verified, or https:// was used against a plain-HTTP server.` + `the certificate could not be verified, or https:// was used against a plain-HTTP server. ` + + `For a certificate the OS does not trust either, set NODE_EXTRA_CA_CERTS to its CA bundle.` ); } const hint = NETWORK_HINTS[code]; diff --git a/src/core/http/system-ca.ts b/src/core/http/system-ca.ts new file mode 100644 index 0000000..e2f00e7 --- /dev/null +++ b/src/core/http/system-ca.ts @@ -0,0 +1,19 @@ +import tls from "node:tls"; + +// Node's default trust store is the bundled Mozilla CA list only, so certificates trusted by +// the OS (corporate proxies, local dev CAs like OrbStack's) fail verification unless the user +// remembers NODE_USE_SYSTEM_CA=1. Merging the system store into the defaults gives every TLS +// connection the same trust the OS has. The APIs exist from Node 22.19 / 24.5; older runtimes +// keep the bundled-only behavior. +export function trustSystemCa(): void { + const canReadSystemStore = typeof tls.getCACertificates === "function"; + const canReplaceDefaults = typeof tls.setDefaultCACertificates === "function"; + if (!canReadSystemStore || !canReplaceDefaults) { + return; + } + const systemCerts = tls.getCACertificates("system"); + if (systemCerts.length === 0) { + return; + } + tls.setDefaultCACertificates([...tls.getCACertificates("default"), ...systemCerts]); +}