























@@ -3,10 +3,15 @@ import fs from "node:fs/promises";
33import path from "node:path";
44import { beforeEach, describe, expect, it, vi } from "vitest";
55import {
6+getWebAuthAgeMs,
7+hasWebCredsSync,
68logoutWeb,
79pickWebChannel,
10+readCredsJsonRaw,
811readWebAuthSnapshot,
912readWebAuthState,
13+readWebSelfId,
14+readWebSelfIdentity,
1015restoreCredsFromBackupIfNeeded,
1116webAuthExists,
1217WhatsAppAuthUnstableError,
@@ -86,6 +91,29 @@ describe("auth-store", () => {
8691});
8792});
889394+it("preserves valid large creds instead of treating them as corrupt", async () => {
95+const authDir = createTempAuthDir("openclaw-wa-auth-large-creds");
96+const credsPath = path.join(authDir, "creds.json");
97+const largeCreds = JSON.stringify({
98+me: { id: "15551234567@s.whatsapp.net" },
99+additionalData: "x".repeat(1024 * 1024 + 512),
100+});
101+fsSync.writeFileSync(credsPath, largeCreds, "utf-8");
102+fsSync.writeFileSync(
103+path.join(authDir, "creds.json.bak"),
104+JSON.stringify({ me: { id: "19990000000@s.whatsapp.net" } }),
105+"utf-8",
106+);
107+108+await expect(webAuthExists(authDir)).resolves.toBe(true);
109+await expect(restoreCredsFromBackupIfNeeded(authDir)).resolves.toBe(false);
110+expect(fsSync.readFileSync(credsPath, "utf-8")).toBe(largeCreds);
111+expect(readWebSelfId(authDir)).toMatchObject({
112+e164: "+15551234567",
113+jid: "15551234567@s.whatsapp.net",
114+});
115+});
116+89117it("refuses to restore creds from a symlinked backup path", async () => {
90118const authDir = createTempAuthDir("openclaw-wa-auth-restore-symlink");
91119const targetPath = path.join(authDir, "backup-target.json");
@@ -99,6 +127,27 @@ describe("auth-store", () => {
99127expect(fsSync.readFileSync(credsPath, "utf-8")).toBe("{");
100128});
101129130+it.runIf(process.platform !== "win32")(
131+"does not restore backup over a symlinked creds path",
132+async () => {
133+const authDir = createTempAuthDir("openclaw-wa-auth-restore-target-symlink");
134+const targetPath = path.join(authDir, "target-creds.json");
135+const credsPath = path.join(authDir, "creds.json");
136+const backupPath = path.join(authDir, "creds.json.bak");
137+fsSync.writeFileSync(targetPath, "{", "utf-8");
138+fsSync.symlinkSync(targetPath, credsPath);
139+fsSync.writeFileSync(
140+backupPath,
141+JSON.stringify({ me: { id: "123@s.whatsapp.net" } }),
142+"utf-8",
143+);
144+145+await expect(restoreCredsFromBackupIfNeeded(authDir)).resolves.toBe(false);
146+expect(fsSync.lstatSync(credsPath).isSymbolicLink()).toBe(true);
147+expect(fsSync.readFileSync(targetPath, "utf-8")).toBe("{");
148+},
149+);
150+102151it("reports linked auth state and snapshot from the shared read helper", async () => {
103152const authDir = createTempAuthDir("openclaw-wa-auth-linked");
104153fsSync.writeFileSync(
@@ -122,6 +171,64 @@ describe("auth-store", () => {
122171});
123172});
124173174+it.runIf(process.platform !== "win32")(
175+"treats symlinked creds as missing across auth readers",
176+async () => {
177+const authDir = createTempAuthDir("openclaw-wa-auth-symlink-read");
178+const targetPath = path.join(authDir, "target-creds.json");
179+const credsPath = path.join(authDir, "creds.json");
180+fsSync.writeFileSync(
181+targetPath,
182+JSON.stringify({ me: { id: "15551234567@s.whatsapp.net" } }),
183+"utf-8",
184+);
185+fsSync.symlinkSync(targetPath, credsPath);
186+187+expect(fsSync.lstatSync(credsPath).isSymbolicLink()).toBe(true);
188+expect(fsSync.statSync(credsPath).isFile()).toBe(true);
189+expect(hasWebCredsSync(authDir)).toBe(false);
190+expect(readCredsJsonRaw(credsPath)).toBeNull();
191+expect(getWebAuthAgeMs(authDir)).toBeNull();
192+expect(readWebSelfId(authDir)).toEqual({ e164: null, jid: null, lid: null });
193+await expect(readWebSelfIdentity(authDir)).resolves.toEqual({
194+e164: null,
195+jid: null,
196+lid: null,
197+});
198+await expect(webAuthExists(authDir)).resolves.toBe(false);
199+await expect(readWebAuthState(authDir)).resolves.toBe("not-linked");
200+await expect(readWebAuthSnapshot(authDir)).resolves.toEqual({
201+state: "not-linked",
202+authAgeMs: null,
203+selfId: { e164: null, jid: null, lid: null },
204+});
205+},
206+);
207+208+it.runIf(process.platform !== "win32")(
209+"treats creds under a symlinked auth directory as missing",
210+async () => {
211+const rootDir = createTempAuthDir("openclaw-wa-auth-symlink-parent");
212+const targetAuthDir = path.join(rootDir, "target-auth");
213+const authDir = path.join(rootDir, "linked-auth");
214+fsSync.mkdirSync(targetAuthDir);
215+fsSync.writeFileSync(
216+path.join(targetAuthDir, "creds.json"),
217+JSON.stringify({ me: { id: "15551234567@s.whatsapp.net" } }),
218+"utf-8",
219+);
220+fsSync.symlinkSync(targetAuthDir, authDir, "dir");
221+const credsPath = path.join(authDir, "creds.json");
222+223+expect(fsSync.lstatSync(authDir).isSymbolicLink()).toBe(true);
224+expect(fsSync.lstatSync(credsPath).isFile()).toBe(true);
225+expect(hasWebCredsSync(authDir)).toBe(false);
226+expect(readCredsJsonRaw(credsPath)).toBeNull();
227+await expect(webAuthExists(authDir)).resolves.toBe(false);
228+await expect(readWebAuthState(authDir)).resolves.toBe("not-linked");
229+},
230+);
231+125232it("reports unstable auth state when the shared barrier read times out", async () => {
126233const authDir = createTempAuthDir("openclaw-wa-auth-unstable-state");
127234fsSync.writeFileSync(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。