Skip to content

Commit 0a554e0

Browse files
security: upgrade ws, fix origin validation, guard layouts
- Upgrade ws from 8.20.0 to 8.21.0 (CVE-2026-48779 memory exhaustion) - Reject Origin: null in WebSocket verifyClient - Add isFinite guard for dagre node positions - Add NaN/Infinity guard for force layout positions
1 parent 4012c78 commit 0a554e0

5 files changed

Lines changed: 26 additions & 19 deletions

File tree

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"helmet": "^8.2.0",
5050
"pdf-lib": "^1.17.1",
5151
"tldraw": "^4.5.0",
52-
"ws": "^8.18.0",
52+
"ws": "^8.21.0",
5353
"zod": "^3.23.0"
5454
},
5555
"devDependencies": {

src/canvas-server.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,23 +48,26 @@ const wss = new WebSocketServer({
4848
const origin = info.origin || info.req.headers.origin
4949
const host = info.req.headers.host
5050

51-
if (!origin && !host) {
52-
callback(true)
51+
if (!origin) {
52+
callback(false, 403, 'Forbidden: missing origin')
5353
return
5454
}
5555

56-
if (origin) {
57-
try {
58-
const url = new URL(origin)
59-
const allowedHosts = ['localhost', '127.0.0.1', host?.split(':')[0]]
60-
if (!allowedHosts.includes(url.hostname)) {
61-
callback(false, 403, 'Forbidden: invalid origin')
62-
return
63-
}
64-
} catch {
56+
if (origin === 'null') {
57+
callback(false, 403, 'Forbidden: null origin')
58+
return
59+
}
60+
61+
try {
62+
const url = new URL(origin)
63+
const allowedHosts = ['localhost', '127.0.0.1', host?.split(':')[0]]
64+
if (!allowedHosts.includes(url.hostname)) {
6565
callback(false, 403, 'Forbidden: invalid origin')
6666
return
6767
}
68+
} catch {
69+
callback(false, 403, 'Forbidden: invalid origin')
70+
return
6871
}
6972

7073
callback(true)

src/layout/dagre.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export function computeDagreLayout(
4545

4646
for (const node of graph.nodes) {
4747
const dagreNode = g.node(node.id)
48-
if (!dagreNode) continue
48+
if (!dagreNode || !isFinite(dagreNode.x) || !isFinite(dagreNode.y)) continue
4949

5050
const x = dagreNode.x - node.width / 2
5151
const y = dagreNode.y - node.height / 2

src/layout/force.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,12 @@ export function computeForceLayout(
6969
const positionMap = new Map<string, { x: number; y: number }>()
7070

7171
for (const node of nodes) {
72-
const x = (node.x ?? 0) - node.width / 2
73-
const y = (node.y ?? 0) - node.height / 2
72+
const rawX = node.x ?? 0
73+
const rawY = node.y ?? 0
74+
const safeX = isFinite(rawX) ? rawX : 0
75+
const safeY = isFinite(rawY) ? rawY : 0
76+
const x = safeX - node.width / 2
77+
const y = safeY - node.height / 2
7478

7579
positions.push({ id: node.id, x, y, width: node.width, height: node.height })
7680
positionMap.set(node.id, { x, y })

0 commit comments

Comments
 (0)