Skip to content

Commit ef694ea

Browse files
committed
Fix clipboard paste validation accepting invalid notes
1 parent d428a2c commit ef694ea

File tree

4 files changed

+114
-16
lines changed

4 files changed

+114
-16
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"scripts": {
77
"dev": "vite",
88
"build": "vite build",
9+
"test": "node --test",
910
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
1011
"preview": "vite preview"
1112
},

src/components/EditorHeader/ControlPanel.jsx

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ import {
4747
} from "../../data/constants";
4848
import jsPDF from "jspdf";
4949
import { useHotkeys } from "react-hotkeys-hook";
50-
import { Validator } from "jsonschema";
51-
import { areaSchema, noteSchema, tableSchema } from "../../data/schemas";
5250
import { db } from "../../data/db";
5351
import {
5452
useLayout,
@@ -67,6 +65,7 @@ import {
6765
} from "../../hooks";
6866
import { enterFullscreen, exitFullscreen } from "../../utils/fullscreen";
6967
import { dataURItoBlob } from "../../utils/utils";
68+
import { classifyClipboardPayload } from "../../utils/clipboard";
7069
import { IconAddArea, IconAddNote, IconAddTable } from "../../icons";
7170
import LayoutDropdown from "./LayoutDropdown";
7271
import Sidesheet from "./SideSheet/Sidesheet";
@@ -710,29 +709,35 @@ export default function ControlPanel({
710709
} catch (error) {
711710
return;
712711
}
713-
const v = new Validator();
714-
console.log(obj);
715-
if (v.validate(obj, tableSchema).valid) {
712+
713+
const clipboardEntity = classifyClipboardPayload(obj);
714+
if (!clipboardEntity) {
715+
return;
716+
}
717+
718+
const { payload } = clipboardEntity;
719+
720+
if (clipboardEntity.type === "table") {
716721
addTable({
717722
table: {
718-
...obj,
719-
x: obj.x + 20,
720-
y: obj.y + 20,
723+
...payload,
724+
x: payload.x + 20,
725+
y: payload.y + 20,
721726
id: nanoid(),
722727
},
723728
});
724-
} else if (v.validate(obj, areaSchema).valid) {
729+
} else if (clipboardEntity.type === "area") {
725730
addArea({
726-
...obj,
727-
x: obj.x + 20,
728-
y: obj.y + 20,
731+
...payload,
732+
x: payload.x + 20,
733+
y: payload.y + 20,
729734
id: areas.length,
730735
});
731-
} else if (v.validate(obj, noteSchema)) {
736+
} else if (clipboardEntity.type === "note") {
732737
addNote({
733-
...obj,
734-
x: obj.x + 20,
735-
y: obj.y + 20,
738+
...payload,
739+
x: payload.x + 20,
740+
y: payload.y + 20,
736741
id: notes.length,
737742
});
738743
}

src/utils/clipboard.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Validator } from "jsonschema";
2+
import { tableSchema, areaSchema, noteSchema } from "../data/schemas";
3+
4+
const validator = new Validator();
5+
6+
const schemaMap = [
7+
{ type: "table", schema: tableSchema },
8+
{ type: "area", schema: areaSchema },
9+
{ type: "note", schema: noteSchema },
10+
];
11+
12+
export function classifyClipboardPayload(payload) {
13+
if (!payload || typeof payload !== "object") {
14+
return null;
15+
}
16+
17+
for (const { type, schema } of schemaMap) {
18+
if (validator.validate(payload, schema).valid) {
19+
return { type, payload };
20+
}
21+
}
22+
23+
return null;
24+
}
25+

tests/clipboard.test.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import test from "node:test";
2+
import assert from "node:assert/strict";
3+
import { classifyClipboardPayload } from "../src/utils/clipboard.js";
4+
5+
const baseField = {
6+
id: "fld_1",
7+
name: "id",
8+
type: "INTEGER",
9+
default: "",
10+
check: "",
11+
primary: true,
12+
unique: true,
13+
notNull: true,
14+
increment: true,
15+
comment: "",
16+
};
17+
18+
test("classifies a valid table payload", () => {
19+
const payload = {
20+
id: "tbl_1",
21+
name: "users",
22+
x: 0,
23+
y: 0,
24+
fields: [baseField],
25+
comment: "",
26+
indices: [],
27+
color: "#000000",
28+
};
29+
30+
const result = classifyClipboardPayload(payload);
31+
assert.ok(result);
32+
assert.equal(result.type, "table");
33+
assert.equal(result.payload, payload);
34+
});
35+
36+
test("classifies a valid note payload", () => {
37+
const payload = {
38+
id: 0,
39+
x: 10,
40+
y: 10,
41+
title: "Note",
42+
content: "",
43+
color: "#ffffff",
44+
height: 80,
45+
};
46+
47+
const result = classifyClipboardPayload(payload);
48+
assert.ok(result);
49+
assert.equal(result.type, "note");
50+
});
51+
52+
test("rejects invalid note payloads", () => {
53+
const payload = {
54+
id: 0,
55+
title: "Broken note",
56+
};
57+
58+
const result = classifyClipboardPayload(payload);
59+
assert.equal(result, null);
60+
});
61+
62+
test("returns null for non-object values", () => {
63+
assert.equal(classifyClipboardPayload(null), null);
64+
assert.equal(classifyClipboardPayload("string"), null);
65+
assert.equal(classifyClipboardPayload(42), null);
66+
});
67+

0 commit comments

Comments
 (0)