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
This commit is contained in:
CodeForgeNet
2026-03-23 04:49:43 +05:30
committed by Peter Steinberger
parent 042669d8c8
commit 3595ecba45
3 changed files with 100 additions and 3 deletions

View File

@@ -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;

View File

@@ -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({

View File

@@ -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";