




















@@ -1,4 +1,4 @@
1-import { execFileSync } from "node:child_process";
1+import * as childProcess from "node:child_process";
22import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
33import fs from "node:fs";
44import os from "node:os";
@@ -73,6 +73,16 @@ type LoadPersistedAuthProfileStoreOptions = {
7373repairOAuthSecretPayloads?: boolean;
7474};
757576+type OAuthProfileSecretKeySeedOptions = { create?: boolean };
77+78+type OAuthProfileSecretKeySeedDeps = {
79+env: NodeJS.ProcessEnv;
80+platform: NodeJS.Platform;
81+readMacKeychain: () => string | undefined;
82+readFile: () => string | undefined;
83+createFile: () => string | undefined;
84+};
85+7686function isRecord(value: unknown): value is Record<string, unknown> {
7787return !!value && typeof value === "object" && !Array.isArray(value);
7888}
@@ -304,7 +314,7 @@ function readMacOAuthProfileSecretKey(): string | undefined {
304314return undefined;
305315}
306316try {
307-return execFileSync(
317+return childProcess.execFileSync(
308318"security",
309319[
310320"find-generic-password",
@@ -321,33 +331,6 @@ function readMacOAuthProfileSecretKey(): string | undefined {
321331}
322332}
323333324-function createMacOAuthProfileSecretKey(): string | undefined {
325-if (process.platform !== "darwin") {
326-return undefined;
327-}
328-const generated = randomBytes(32).toString("base64url");
329-try {
330-execFileSync(
331-"security",
332-[
333-"add-generic-password",
334-"-U",
335-"-s",
336-OAUTH_PROFILE_SECRET_KEYCHAIN_SERVICE,
337-"-a",
338-OAUTH_PROFILE_SECRET_KEYCHAIN_ACCOUNT,
339-"-w",
340-generated,
341-],
342-{ encoding: "utf8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"] },
343-);
344-return generated;
345-} catch (err) {
346-log.warn("failed to create oauth profile secret keychain entry", { err });
347-return undefined;
348-}
349-}
350-351334function isPathInsideOrEqual(parentDir: string, candidatePath: string): boolean {
352335const relative = path.relative(path.resolve(parentDir), path.resolve(candidatePath));
353336return (
@@ -459,39 +442,53 @@ function createFallbackOAuthProfileSecretKeyFile(): string | undefined {
459442}
460443}
461444462-function shouldUseMacKeychainForOAuthProfileSecrets(): boolean {
445+function shouldReadMacKeychainForOAuthProfileSecrets(params?: {
446+env?: NodeJS.ProcessEnv;
447+platform?: NodeJS.Platform;
448+}): boolean {
449+const env = params?.env ?? process.env;
450+const platform = params?.platform ?? process.platform;
463451return (
464-process.platform === "darwin" &&
465-process.env.VITEST !== "true" &&
466-process.env.VITEST_WORKER_ID === undefined
452+platform === "darwin" && env.VITEST !== "true" && env.VITEST_WORKER_ID === undefined
467453);
468454}
469455470-function resolveOAuthProfileSecretKeySeed(options?: { create?: boolean }): string | undefined {
471-const externalKey = process.env[OAUTH_PROFILE_SECRET_KEY_ENV]?.trim();
456+function resolveOAuthProfileSecretKeySeedWithDeps(
457+options: OAuthProfileSecretKeySeedOptions | undefined,
458+deps: OAuthProfileSecretKeySeedDeps,
459+): string | undefined {
460+const externalKey = deps.env[OAUTH_PROFILE_SECRET_KEY_ENV]?.trim();
472461if (externalKey) {
473462return externalKey;
474463}
475-if (process.env.NODE_ENV === "test" && process.env.VITEST === "true") {
464+if (deps.env.NODE_ENV === "test" && deps.env.VITEST === "true") {
476465return "openclaw-test-oauth-profile-secret-key";
477466}
478-if (shouldUseMacKeychainForOAuthProfileSecrets()) {
479-const keychainKey =
480-readMacOAuthProfileSecretKey() ??
481-(options?.create === true ? createMacOAuthProfileSecretKey() : undefined);
467+if (shouldReadMacKeychainForOAuthProfileSecrets({ env: deps.env, platform: deps.platform })) {
468+const keychainKey = deps.readMacKeychain();
482469if (keychainKey) {
483470return keychainKey;
484471}
485472}
486-const fileKey =
487-readFallbackOAuthProfileSecretKeyFile() ??
488-(options?.create === true ? createFallbackOAuthProfileSecretKeyFile() : undefined);
473+const fileKey = deps.readFile() ?? (options?.create === true ? deps.createFile() : undefined);
489474if (fileKey) {
490475return fileKey;
491476}
492477return undefined;
493478}
494479480+function resolveOAuthProfileSecretKeySeed(
481+options?: OAuthProfileSecretKeySeedOptions,
482+): string | undefined {
483+return resolveOAuthProfileSecretKeySeedWithDeps(options, {
484+env: process.env,
485+platform: process.platform,
486+readMacKeychain: readMacOAuthProfileSecretKey,
487+readFile: readFallbackOAuthProfileSecretKeyFile,
488+createFile: createFallbackOAuthProfileSecretKeyFile,
489+});
490+}
491+495492function buildOAuthProfileSecretKey(options?: { create?: boolean }): Buffer | null {
496493const externalKey = resolveOAuthProfileSecretKeySeed(options);
497494if (!externalKey) {
@@ -500,6 +497,11 @@ function buildOAuthProfileSecretKey(options?: { create?: boolean }): Buffer | nu
500497return createHash("sha256").update(`openclaw:auth-profile-oauth:${externalKey}`).digest();
501498}
502499500+export const __testing = {
501+ resolveOAuthProfileSecretKeySeedWithDeps,
502+ shouldReadMacKeychainForOAuthProfileSecrets,
503+};
504+503505function encryptOAuthProfileSecretMaterial(params: {
504506ref: OAuthCredentialRef;
505507profileId: string;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。