mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-01-02 23:08:49 +01:00
🎨 Improve error handling and security for plugin data storage methods (#16717)
- Check if storageName contains path separators to prevent path injection - saveData and removeData return complete IWebSocketData objects with code, msg, and data fields for result checking - saveData and removeData immediately modify this.data[storageName] at function start, so even if the operation fails, subsequent use of this.data[storageName] is not affected - All methods always resolve
This commit is contained in:
parent
3c00f5a3d1
commit
c243fea81c
1 changed files with 62 additions and 11 deletions
|
|
@ -259,12 +259,17 @@ export class Plugin {
|
|||
}
|
||||
|
||||
public loadData(storageName: string) {
|
||||
if (storageName.includes("/") || storageName.includes("\\")) {
|
||||
console.error(`plugin ${this.name} loadData failed: storageName cannot contain path separators`);
|
||||
return Promise.resolve(this.data[storageName]);
|
||||
}
|
||||
|
||||
if (typeof this.data[storageName] === "undefined") {
|
||||
this.data[storageName] = "";
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
fetchPost("/api/file/getFile", {path: `/data/storage/petal/${this.name}/${storageName}`}, (response) => {
|
||||
if (response.code !== 404) {
|
||||
if (response.code !== 404 && response.code !== 403) {
|
||||
this.data[storageName] = response;
|
||||
}
|
||||
resolve(this.data[storageName]);
|
||||
|
|
@ -273,34 +278,78 @@ export class Plugin {
|
|||
}
|
||||
|
||||
public saveData(storageName: string, data: any) {
|
||||
if (storageName.includes("/") || storageName.includes("\\")) {
|
||||
console.error(`plugin ${this.name} saveData failed: storageName cannot contain path separators`);
|
||||
return Promise.resolve({
|
||||
code: -1,
|
||||
msg: "storageName cannot contain path separators",
|
||||
data: null
|
||||
} as IWebSocketData);
|
||||
}
|
||||
|
||||
this.data[storageName] = data;
|
||||
|
||||
if (window.siyuan.config.readonly || window.siyuan.isPublish) {
|
||||
return;
|
||||
console.warn(`plugin ${this.name} saveData failed: Readonly mode or publish mode`);
|
||||
return Promise.resolve({
|
||||
code: 403,
|
||||
msg: "Readonly mode or publish mode",
|
||||
data: null
|
||||
} as IWebSocketData);
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const pathString = `/data/storage/petal/${this.name}/${storageName}`;
|
||||
let file: File;
|
||||
if (typeof data === "object") {
|
||||
file = new File([new Blob([JSON.stringify(data)], {
|
||||
type: "application/json"
|
||||
})], pathString.split("/").pop());
|
||||
} else {
|
||||
file = new File([new Blob([data])], pathString.split("/").pop());
|
||||
try {
|
||||
if (typeof data === "object") {
|
||||
file = new File([new Blob([JSON.stringify(data)], {
|
||||
type: "application/json"
|
||||
})], pathString.split("/").pop());
|
||||
} else {
|
||||
file = new File([new Blob([data])], pathString.split("/").pop());
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`plugin ${this.name} saveData failed:`, e);
|
||||
resolve({
|
||||
code: -1,
|
||||
msg: e instanceof Error ? e.message : String(e),
|
||||
data: null
|
||||
} as IWebSocketData);
|
||||
return;
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append("path", pathString);
|
||||
formData.append("file", file);
|
||||
formData.append("isDir", "false");
|
||||
fetchPost("/api/file/putFile", formData, (response) => {
|
||||
this.data[storageName] = data;
|
||||
if (typeof response === "object" && response.code !== 0) {
|
||||
console.error(`plugin ${this.name} saveData failed:`, response);
|
||||
}
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public removeData(storageName: string) {
|
||||
if (storageName.includes("/") || storageName.includes("\\")) {
|
||||
console.error(`plugin ${this.name} removeData failed: storageName cannot contain path separators`);
|
||||
return Promise.resolve({
|
||||
code: -1,
|
||||
msg: "storageName cannot contain path separators",
|
||||
data: null
|
||||
} as IWebSocketData);
|
||||
}
|
||||
|
||||
delete this.data[storageName];
|
||||
|
||||
if (window.siyuan.config.readonly || window.siyuan.isPublish) {
|
||||
return;
|
||||
console.warn(`plugin ${this.name} removeData failed: Readonly mode or publish mode`);
|
||||
return Promise.resolve({
|
||||
code: 403,
|
||||
msg: "Readonly mode or publish mode",
|
||||
data: null
|
||||
} as IWebSocketData);
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
|
|
@ -308,7 +357,9 @@ export class Plugin {
|
|||
this.data = {};
|
||||
}
|
||||
fetchPost("/api/file/removeFile", {path: `/data/storage/petal/${this.name}/${storageName}`}, (response) => {
|
||||
delete this.data[storageName];
|
||||
if (response.code !== 0 && response.code !== 404) {
|
||||
console.error(`plugin ${this.name} removeData failed:`, response);
|
||||
}
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue