Skip to content

Commit 27006fe

Browse files
authored
🐛(sync) WebDAV verify() 过弱 (#1395)
1 parent 206b5fe commit 27006fe

2 files changed

Lines changed: 74 additions & 3 deletions

File tree

packages/filesystem/webdav/webdav.test.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,24 @@ describe("WebDAVFileSystem", () => {
7070
});
7171

7272
describe("verify", () => {
73-
it("应当成功验证", async () => {
73+
it("应当通过列目录、写入探针文件和清理探针完成验证", async () => {
7474
const fs = createTestFS(mockClient);
7575

7676
await expect(fs.verify()).resolves.toBeUndefined();
7777
expect(mockClient.getQuota).toHaveBeenCalled();
78+
expect(mockClient.getDirectoryContents).toHaveBeenCalledWith("/");
79+
expect(mockClient.createDirectory).toHaveBeenCalledWith(expect.stringMatching(/^\/\.scriptcat-verify-/));
80+
expect(mockClient.putFileContents).toHaveBeenCalledWith(
81+
expect.stringMatching(/^\/\.scriptcat-verify-.+\/probe\.txt$/),
82+
""
83+
);
84+
expect(mockClient.deleteFile).toHaveBeenCalledWith(
85+
expect.stringMatching(/^\/\.scriptcat-verify-.+\/probe\.txt$/)
86+
);
87+
expect(mockClient.deleteFile).toHaveBeenCalledWith(expect.stringMatching(/^\/\.scriptcat-verify-/));
7888
});
7989

80-
it("应当在 401 时抛出 WarpTokenError", async () => {
90+
it("应当在 401 时抛出 WarpTokenError 1", async () => {
8191
(mockClient.getQuota as ReturnType<typeof vi.fn>).mockRejectedValue({
8292
response: { status: 401 },
8393
message: "Unauthorized",
@@ -87,14 +97,48 @@ describe("WebDAVFileSystem", () => {
8797
await expect(fs.verify()).rejects.toBeInstanceOf(WarpTokenError);
8898
});
8999

90-
it("应当在其他错误时抛出包含原始信息的 Error", async () => {
100+
it("应当在 401 时抛出 WarpTokenError 2", async () => {
101+
(mockClient.getDirectoryContents as ReturnType<typeof vi.fn>).mockRejectedValue({
102+
response: { status: 401 },
103+
message: "Unauthorized",
104+
});
105+
const fs = createTestFS(mockClient);
106+
107+
await expect(fs.verify()).rejects.toBeInstanceOf(WarpTokenError);
108+
});
109+
110+
it("应当在其他错误时抛出包含原始信息的 Error 1", async () => {
91111
(mockClient.getQuota as ReturnType<typeof vi.fn>).mockRejectedValue({
92112
message: "Network error",
93113
});
94114
const fs = createTestFS(mockClient);
95115

96116
await expect(fs.verify()).rejects.toThrow("WebDAV verify failed: Network error");
97117
});
118+
119+
it("应当在其他错误时抛出包含原始信息的 Error 2", async () => {
120+
(mockClient.getDirectoryContents as ReturnType<typeof vi.fn>).mockRejectedValue({
121+
message: "Network error",
122+
});
123+
const fs = createTestFS(mockClient);
124+
125+
await expect(fs.verify()).rejects.toThrow("WebDAV verify failed: Network error");
126+
});
127+
128+
it("应当在无法写入探针文件时验证失败并清理探针目录", async () => {
129+
(mockClient.putFileContents as ReturnType<typeof vi.fn>).mockResolvedValue(false);
130+
const fs = createTestFS(mockClient);
131+
132+
await expect(fs.verify()).rejects.toThrow("WebDAV verify failed: probe file write returned false");
133+
expect(mockClient.deleteFile).toHaveBeenCalledWith(expect.stringMatching(/^\/\.scriptcat-verify-/));
134+
});
135+
136+
it("应当在删除探针文件失败时验证失败", async () => {
137+
(mockClient.deleteFile as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error("Delete denied"));
138+
const fs = createTestFS(mockClient);
139+
140+
await expect(fs.verify()).rejects.toThrow("WebDAV verify failed: Delete denied");
141+
});
98142
});
99143

100144
describe("openDir", () => {

packages/filesystem/webdav/webdav.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,43 @@ export default class WebDAVFileSystem implements FileSystem {
5353
}
5454

5555
async verify(): Promise<void> {
56+
const verifyDir = joinPath(this.basePath, `.scriptcat-verify-${Date.now()}-${Math.random().toString(36).slice(2)}`);
57+
const verifyFile = joinPath(verifyDir, "probe.txt");
58+
let dirCreated = false;
59+
let fileCreated = false;
60+
5661
try {
5762
await this.client.getQuota();
63+
await this.client.getDirectoryContents(this.basePath);
64+
await this.client.createDirectory(verifyDir);
65+
dirCreated = true;
66+
const written = await this.client.putFileContents(verifyFile, "");
67+
if (!written) {
68+
throw new Error("probe file write returned false");
69+
}
70+
fileCreated = true;
71+
await this.client.deleteFile(verifyFile);
72+
fileCreated = false;
73+
await this.client.deleteFile(verifyDir);
74+
dirCreated = false;
5875
} catch (e: any) {
76+
await this.cleanupVerifyProbe(verifyFile, verifyDir, fileCreated, dirCreated);
5977
if (e.response?.status === 401) {
6078
throw new WarpTokenError(e);
6179
}
6280
throw new Error(`WebDAV verify failed: ${e.message}`); // 保留原始信息
6381
}
6482
}
6583

84+
private async cleanupVerifyProbe(verifyFile: string, verifyDir: string, fileCreated: boolean, dirCreated: boolean) {
85+
if (fileCreated) {
86+
await this.client.deleteFile(verifyFile).catch(() => undefined);
87+
}
88+
if (dirCreated) {
89+
await this.client.deleteFile(verifyDir).catch(() => undefined);
90+
}
91+
}
92+
6693
async open(file: FileInfo): Promise<FileReader> {
6794
return new WebDAVFileReader(this.client, joinPath(file.path, file.name));
6895
}

0 commit comments

Comments
 (0)