fix(server/search): invalid canvas crashing search (closes #9004)
Some checks are pending
Checks / main (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Documentation / Build and Deploy Documentation (push) Waiting to run
Dev / Test development (push) Waiting to run
Dev / Build Docker image (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile) (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile.alpine) (push) Blocked by required conditions
/ Check Docker build (Dockerfile) (push) Waiting to run
/ Check Docker build (Dockerfile.alpine) (push) Waiting to run
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.alpine, ubuntu-latest, linux/amd64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v7) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v8) (push) Blocked by required conditions
/ Merge manifest lists (push) Blocked by required conditions
playwright / E2E tests on linux-arm64 (push) Waiting to run
playwright / E2E tests on linux-x64 (push) Waiting to run

This commit is contained in:
Elian Doran 2026-03-11 18:32:53 +02:00
parent cb5b4d870f
commit 3de712aca4
No known key found for this signature in database
2 changed files with 51 additions and 19 deletions

View File

@ -15,4 +15,26 @@ describe("Mind map preprocessing", () => {
expect(preprocessContent("", type, mime)).toEqual("");
expect(preprocessContent(`{ "node": " }`, type, mime)).toEqual("");
});
it("reads data", () => {
expect(preprocessContent(`{ "nodedata": { "topic": "Root", "children": [ { "topic": "Child 1" }, { "topic": "Child 2", "children": [ { "topic": "Grandchild" } ] } ] } }`, type, mime)).toEqual("root, child 1, child 2, grandchild");
});
});
describe("Canvas preprocessing", () => {
const type: NoteType = "canvas";
const mime = "application/json";
it("supports empty JSON", () => {
expect(preprocessContent("{}", type, mime)).toEqual("");
});
it("supports blank text / invalid JSON", () => {
expect(preprocessContent("", type, mime)).toEqual("");
});
it("reads elements", () => {
expect(preprocessContent(`{ "elements": [ { "type": "text", "text": "Hello" } ] }`, type, mime)).toEqual("hello");
expect(preprocessContent(`{ "elements": [ { "type": "text" }, { "type": "text", "text": "World" }, { "type": "rectangle", "text": "Ignored" } ] }`, type, mime)).toEqual("world");
});
});

View File

@ -15,25 +15,7 @@ export default function preprocessContent(content: string | Buffer, type: string
} else if (type === "mindMap" && mime === "application/json") {
content = processMindmapContent(content);
} else if (type === "canvas" && mime === "application/json") {
interface Element {
type: string;
text?: string; // Optional since not all objects have a `text` property
id: string;
[key: string]: any; // Other properties that may exist
}
const canvasContent = JSON.parse(content);
const elements = canvasContent.elements;
if (Array.isArray(elements)) {
const texts = elements
.filter((element: Element) => element.type === "text" && element.text) // Filter for 'text' type elements with a 'text' property
.map((element: Element) => element.text!); // Use `!` to assert `text` is defined after filtering
content = normalize(texts.join(" "));
} else {
content = "";
}
content = processCanvasContent(content);
}
return content.trim();
@ -98,6 +80,34 @@ function processMindmapContent(content: string) {
return normalize(topicsString.toString());
}
function processCanvasContent(content: string) {
interface Element {
type: string;
text?: string; // Optional since not all objects have a `text` property
id: string;
[key: string]: any; // Other properties that may exist
}
let canvasContent;
try {
canvasContent = JSON.parse(content);
} catch (e) {
return "";
}
const elements = canvasContent.elements;
if (Array.isArray(elements)) {
const texts = elements
.filter((element: Element) => element.type === "text" && element.text) // Filter for 'text' type elements with a 'text' property
.map((element: Element) => element.text!); // Use `!` to assert `text` is defined after filtering
content = normalize(texts.join(" "));
} else {
content = "";
}
return content;
}
function stripTags(content: string) {
// we want to allow link to preserve URLs: https://github.com/zadam/trilium/issues/2412
// we want to insert space in place of block tags (because they imply text separation)