From 0031ef3120bd60a4b58b0933a2547a53c444ccc8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 3 May 2026 13:10:00 +0100 Subject: [PATCH] refactor: keep legacy secretref migration in doctor --- .../doctor-legacy-config.migrations.test.ts | 2 +- src/config/validation.policy.test.ts | 24 +++----------- src/config/validation.ts | 31 +------------------ src/secrets/legacy-secretref-env-marker.ts | 22 ------------- 4 files changed, 6 insertions(+), 73 deletions(-) diff --git a/src/commands/doctor-legacy-config.migrations.test.ts b/src/commands/doctor-legacy-config.migrations.test.ts index e0d5c4874b3..9b3c1ff6268 100644 --- a/src/commands/doctor-legacy-config.migrations.test.ts +++ b/src/commands/doctor-legacy-config.migrations.test.ts @@ -271,7 +271,7 @@ describe("normalizeCompatibilityConfigValues", () => { ); }); - it("leaves invalid legacy secretref-env markers for validation to reject", () => { + it("leaves invalid legacy secretref-env markers unchanged", () => { const res = normalizeCompatibilityConfigValues({ messages: { groupChat: { diff --git a/src/config/validation.policy.test.ts b/src/config/validation.policy.test.ts index 2c90db19291..7b681aa82d9 100644 --- a/src/config/validation.policy.test.ts +++ b/src/config/validation.policy.test.ts @@ -98,7 +98,7 @@ describe("config validation SecretRef policy guards", () => { expect(result.ok).toBe(true); }); - it("rejects legacy secretref-env markers on supported SecretRef credential paths", () => { + it("leaves legacy secretref-env marker migration to doctor", () => { const result = validateConfigObjectRaw({ secrets: { defaults: { @@ -112,21 +112,10 @@ describe("config validation SecretRef policy guards", () => { }, }); - expect(result.ok).toBe(false); - if (!result.ok) { - const issue = result.issues.find((entry) => entry.path === "channels.discord.token"); - expect(issue).toBeDefined(); - expect(issue?.message).toContain( - '"secretref-env:DISCORD_BOT_TOKEN" is a legacy SecretRef marker', - ); - expect(issue?.message).toContain( - '{"source":"env","provider":"gateway-env","id":"DISCORD_BOT_TOKEN"}', - ); - expect(issue?.message).toContain('Run "openclaw doctor --fix"'); - } + expect(result.ok).toBe(true); }); - it("rejects invalid legacy secretref-env markers that doctor cannot migrate", () => { + it("does not reject invalid legacy secretref-env markers during raw validation", () => { const result = validateConfigObjectRaw({ channels: { discord: { @@ -135,12 +124,7 @@ describe("config validation SecretRef policy guards", () => { }, }); - expect(result.ok).toBe(false); - if (!result.ok) { - const issue = result.issues.find((entry) => entry.path === "channels.discord.token"); - expect(issue?.message).toContain('"secretref-env:not-valid" is a legacy SecretRef marker'); - expect(issue?.message).toContain('{"source":"env","provider":"default","id":"ENV_VAR"}'); - } + expect(result.ok).toBe(true); }); it("replaces derived unrecognized-key errors with policy guidance for discord thread binding webhookToken", () => { diff --git a/src/config/validation.ts b/src/config/validation.ts index b626d68e614..0836590f8b0 100644 --- a/src/config/validation.ts +++ b/src/config/validation.ts @@ -19,7 +19,6 @@ import { import { validateJsonSchemaValue } from "../plugins/schema-validator.js"; import { hasKind } from "../plugins/slots.js"; import { resolveWebSearchInstallCatalogEntries } from "../plugins/web-search-install-catalog.js"; -import { collectLegacySecretRefEnvMarkerCandidates } from "../secrets/legacy-secretref-env-marker.js"; import { collectUnsupportedSecretRefConfigCandidates } from "../secrets/unsupported-surface-policy.js"; import { hasAvatarUriScheme, @@ -492,35 +491,7 @@ function mergeUnsupportedMutableSecretRefIssues( } export function collectUnsupportedSecretRefPolicyIssues(raw: unknown): ConfigValidationIssue[] { - return [ - ...collectUnsupportedMutableSecretRefIssues(raw), - ...collectLegacySecretRefEnvMarkerIssues(raw), - ]; -} - -function formatLegacySecretRefEnvMarkerMessage(candidate: { - value: string; - ref: { id: string; provider: string } | null; -}): string { - const replacement = candidate.ref - ? JSON.stringify({ source: "env", provider: candidate.ref.provider, id: candidate.ref.id }) - : '{"source":"env","provider":"default","id":"ENV_VAR"}'; - return [ - `${JSON.stringify(candidate.value)} is a legacy SecretRef marker and is not valid openclaw.json config.`, - `Use a structured SecretRef object instead, for example ${replacement}.`, - 'Run "openclaw doctor --fix" to migrate valid secretref-env: markers.', - `See ${SECRETREF_POLICY_DOC_URL}.`, - ].join(" "); -} - -function collectLegacySecretRefEnvMarkerIssues(raw: unknown): ConfigValidationIssue[] { - if (!isRecord(raw)) { - return []; - } - return collectLegacySecretRefEnvMarkerCandidates(raw as OpenClawConfig).map((candidate) => ({ - path: candidate.path, - message: formatLegacySecretRefEnvMarkerMessage(candidate), - })); + return collectUnsupportedMutableSecretRefIssues(raw); } function mapZodIssueToConfigIssue(issue: unknown): ConfigValidationIssue { diff --git a/src/secrets/legacy-secretref-env-marker.ts b/src/secrets/legacy-secretref-env-marker.ts index 2c56e888222..a851eeb24a1 100644 --- a/src/secrets/legacy-secretref-env-marker.ts +++ b/src/secrets/legacy-secretref-env-marker.ts @@ -21,25 +21,6 @@ function isLegacySecretRefEnvMarker(value: unknown): value is string { return typeof value === "string" && value.trim().startsWith(LEGACY_SECRETREF_ENV_MARKER_PREFIX); } -function containsLegacySecretRefEnvMarker(value: unknown, seen = new WeakSet()): boolean { - if (isLegacySecretRefEnvMarker(value)) { - return true; - } - if (!value || typeof value !== "object") { - return false; - } - if (seen.has(value)) { - return false; - } - seen.add(value); - if (Array.isArray(value)) { - return value.some((entry) => containsLegacySecretRefEnvMarker(entry, seen)); - } - return Object.values(value as Record).some((entry) => - containsLegacySecretRefEnvMarker(entry, seen), - ); -} - function toCandidate( target: DiscoveredConfigSecretTarget, defaults: NonNullable["defaults"] | undefined, @@ -58,9 +39,6 @@ function toCandidate( export function collectLegacySecretRefEnvMarkerCandidates( config: OpenClawConfig, ): LegacySecretRefEnvMarkerCandidate[] { - if (!containsLegacySecretRefEnvMarker(config)) { - return []; - } const defaults = config.secrets?.defaults; return discoverConfigSecretTargets(config) .map((target) => toCandidate(target, defaults))