From 3595ecba45cbcbd5f82b37ac72023371c7032162 Mon Sep 17 00:00:00 2001 From: CodeForgeNet Date: Mon, 23 Mar 2026 04:49:43 +0530 Subject: [PATCH] fix(gateway): pass process.env in status command probe auth to resolve SecretRef Fixes #52360 resolveGatewayProbeAuthSafe was called from status-all.ts without an env argument, causing the credential resolution chain to fall back to an empty object instead of process.env. This made env-backed SecretRef tokens (gateway.auth.token, Telegram botToken, etc.) appear unresolved in the status command path even when the runtime was healthy. Added process.env as default fallback in buildGatewayProbeCredentialPolicy and passed env explicitly from status-all.ts callers. Related: #33070, #38973, #39415, #46014, #49730 --- src/commands/status-all.ts | 14 +++++++-- src/gateway/probe-auth.test.ts | 55 ++++++++++++++++++++++++++++++++++ src/gateway/probe-auth.ts | 34 +++++++++++++++++++++ 3 files changed, 100 insertions(+), 3 deletions(-) diff --git a/src/commands/status-all.ts b/src/commands/status-all.ts index 99a4e8bdc9e..fed39420cbd 100644 --- a/src/commands/status-all.ts +++ b/src/commands/status-all.ts @@ -14,7 +14,7 @@ import type { GatewayService } from "../daemon/service.js"; import { resolveGatewayService } from "../daemon/service.js"; import { buildGatewayConnectionDetails, callGateway } from "../gateway/call.js"; import { normalizeControlUiBasePath } from "../gateway/control-ui-shared.js"; -import { resolveGatewayProbeAuthSafe } from "../gateway/probe-auth.js"; +import { resolveGatewayProbeAuthSafeWithSecretInputs } from "../gateway/probe-auth.js"; import { probeGateway } from "../gateway/probe.js"; import { collectChannelStatusIssues } from "../infra/channels-status-issues.js"; import { resolveOpenClawPackageRoot } from "../infra/openclaw-root.js"; @@ -124,8 +124,16 @@ export async function statusAllCommand( const remoteUrlMissing = isRemoteMode && !remoteUrlRaw; const gatewayMode = isRemoteMode ? "remote" : "local"; - const localProbeAuthResolution = resolveGatewayProbeAuthSafe({ cfg, mode: "local" }); - const remoteProbeAuthResolution = resolveGatewayProbeAuthSafe({ cfg, mode: "remote" }); + const localProbeAuthResolution = await resolveGatewayProbeAuthSafeWithSecretInputs({ + cfg, + mode: "local", + env: process.env, + }); + const remoteProbeAuthResolution = await resolveGatewayProbeAuthSafeWithSecretInputs({ + cfg, + mode: "remote", + env: process.env, + }); const probeAuthResolution = isRemoteMode && !remoteUrlMissing ? remoteProbeAuthResolution : localProbeAuthResolution; const probeAuth = probeAuthResolution.auth; diff --git a/src/gateway/probe-auth.test.ts b/src/gateway/probe-auth.test.ts index bbf034c882f..b95eebf58db 100644 --- a/src/gateway/probe-auth.test.ts +++ b/src/gateway/probe-auth.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { resolveGatewayProbeAuthSafe, + resolveGatewayProbeAuthSafeWithSecretInputs, resolveGatewayProbeAuthWithSecretInputs, } from "./probe-auth.js"; @@ -107,6 +108,60 @@ describe("resolveGatewayProbeAuthSafe", () => { }); }); +describe("resolveGatewayProbeAuthSafeWithSecretInputs", () => { + it("resolves env SecretRef token via async secret-inputs path", async () => { + const result = await resolveGatewayProbeAuthSafeWithSecretInputs({ + cfg: { + gateway: { + auth: { + mode: "token", + token: { source: "env", provider: "default", id: "OPENCLAW_GATEWAY_TOKEN" }, + }, + }, + secrets: { + providers: { + default: { source: "env" }, + }, + }, + } as OpenClawConfig, + mode: "local", + env: { + OPENCLAW_GATEWAY_TOKEN: "test-token-from-env", + } as NodeJS.ProcessEnv, + }); + + expect(result.warning).toBeUndefined(); + expect(result.auth).toEqual({ + token: "test-token-from-env", + password: undefined, + }); + }); + + it("returns warning and empty auth when SecretRef cannot be resolved via async path", async () => { + const result = await resolveGatewayProbeAuthSafeWithSecretInputs({ + cfg: { + gateway: { + auth: { + mode: "token", + token: { source: "env", provider: "default", id: "MISSING_TOKEN_XYZ" }, + }, + }, + secrets: { + providers: { + default: { source: "env" }, + }, + }, + } as OpenClawConfig, + mode: "local", + env: {} as NodeJS.ProcessEnv, + }); + + expect(result.auth).toEqual({}); + expect(result.warning).toContain("gateway.auth.token"); + expect(result.warning).toContain("unresolved"); + }); +}); + describe("resolveGatewayProbeAuthWithSecretInputs", () => { it("resolves local probe SecretRef values before shared credential selection", async () => { const auth = await resolveGatewayProbeAuthWithSecretInputs({ diff --git a/src/gateway/probe-auth.ts b/src/gateway/probe-auth.ts index 2c624acaa00..fe14fdabd64 100644 --- a/src/gateway/probe-auth.ts +++ b/src/gateway/probe-auth.ts @@ -50,6 +50,40 @@ export async function resolveGatewayProbeAuthWithSecretInputs(params: { }); } +export async function resolveGatewayProbeAuthSafeWithSecretInputs(params: { + cfg: OpenClawConfig; + mode: "local" | "remote"; + env?: NodeJS.ProcessEnv; + explicitAuth?: ExplicitGatewayAuth; +}): Promise<{ + auth: { token?: string; password?: string }; + warning?: string; +}> { + const explicitToken = params.explicitAuth?.token?.trim(); + const explicitPassword = params.explicitAuth?.password?.trim(); + if (explicitToken || explicitPassword) { + return { + auth: { + ...(explicitToken ? { token: explicitToken } : {}), + ...(explicitPassword ? { password: explicitPassword } : {}), + }, + }; + } + + try { + const auth = await resolveGatewayProbeAuthWithSecretInputs(params); + return { auth }; + } catch (error) { + if (!isGatewaySecretRefUnavailableError(error)) { + throw error; + } + return { + auth: {}, + warning: `${error.path} SecretRef is unresolved in this command path; probing without configured auth credentials.`, + }; + } +} + export function resolveGatewayProbeAuthSafe(params: { cfg: OpenClawConfig; mode: "local" | "remote";