Skip to content

Commit 93f58ec

Browse files
committed
fix(server): use xss to serialize data
Signed-off-by: Innei <tukon479@gmail.com>
1 parent 81d49f0 commit 93f58ec

File tree

4 files changed

+34
-53
lines changed

4 files changed

+34
-53
lines changed

apps/server/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"satori": "0.11.2",
3535
"sonner": "^1.5.0",
3636
"tailwindcss": "3.4.14",
37-
"use-context-selector": "2.0.0"
37+
"use-context-selector": "2.0.0",
38+
"xss": "1.0.15"
3839
},
3940
"devDependencies": {
4041
"@follow/components": "workspace:*",
@@ -45,14 +46,12 @@
4546
"@follow/types": "workspace:*",
4647
"@follow/utils": "workspace:*",
4748
"@radix-ui/react-avatar": "1.1.1",
48-
"@types/serialize-javascript": "5.0.4",
4949
"daisyui": "4.12.13",
5050
"foxact": "0.2.39",
5151
"lightningcss": "1.27.0",
5252
"lodash-es": "4.17.21",
5353
"masonic": "4.0.1",
5454
"react-dom": "^18.3.1",
55-
"serialize-javascript": "6.0.2",
5655
"tailwindcss": "3.4.14",
5756
"tsup": "8.3.0",
5857
"tsx": "4.19.1",

apps/server/src/lib/seo.ts

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,4 @@
1-
import serialize from "serialize-javascript"
2-
3-
function escapeHtml(unsafe?: string | null) {
4-
if (!unsafe) {
5-
return unsafe
6-
}
7-
8-
return unsafe
9-
.replaceAll("&", "&amp;")
10-
.replaceAll("<", "&lt;")
11-
.replaceAll(">", "&gt;")
12-
.replaceAll('"', "&quot;")
13-
.replaceAll("'", "&#x27;")
14-
.replaceAll("/", "&#x2F;")
15-
}
1+
import xss from "xss"
162

173
export function buildSeoMetaTags(configs: {
184
openGraph: {
@@ -22,25 +8,23 @@ export function buildSeoMetaTags(configs: {
228
}
239
}) {
2410
const openGraph = {
25-
title: escapeHtml(configs.openGraph.title),
26-
description: escapeHtml(configs.openGraph.description),
27-
image: escapeHtml(configs.openGraph.image),
11+
title: xss(configs.openGraph.title),
12+
description: xss(configs.openGraph.description ?? ""),
13+
image: xss(configs.openGraph.image ?? ""),
2814
}
2915
const title = `${openGraph.title} | Follow`
3016
return [
31-
`<meta property="og:title" content="${serialize(title)}" />`,
17+
`<meta property="og:title" content="${title}" />`,
3218
openGraph.description
33-
? `<meta property="og:description" content="${serialize(openGraph.description)}" />`
19+
? `<meta property="og:description" content="${openGraph.description}" />`
3420
: "",
35-
openGraph.image ? `<meta property="og:image" content="${serialize(openGraph.image)}" />` : "",
21+
openGraph.image ? `<meta property="og:image" content="${openGraph.image}" />` : "",
3622
// Twitter
3723
`<meta property="twitter:card" content="summary_large_image" />`,
38-
`<meta property="twitter:title" content="${serialize(title)}" />`,
24+
`<meta property="twitter:title" content="${title}" />`,
3925
openGraph.description
40-
? `<meta property="twitter:description" content="${serialize(openGraph.description)}" />`
41-
: "",
42-
openGraph.image
43-
? `<meta property="twitter:image" content="${serialize(openGraph.image)}" />`
26+
? `<meta property="twitter:description" content="${openGraph.description}" />`
4427
: "",
28+
openGraph.image ? `<meta property="twitter:image" content="${openGraph.image}" />` : "",
4529
].join("\n")
4630
}

apps/server/src/router/global.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { env } from "@follow/shared/env"
66
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"
77
import { parseHTML } from "linkedom"
88
import { FetchError } from "ofetch"
9-
import serialize from "serialize-javascript"
9+
import xss from "xss"
1010

1111
import { isDev } from "~/lib/env"
1212
import { buildSeoMetaTags } from "~/lib/seo"
@@ -119,14 +119,12 @@ async function injectMetaToTemplate(template: string, req: FastifyRequest) {
119119
break
120120
}
121121
case "meta": {
122-
allMetaString.push(
123-
`<meta property="${meta.property}" content="${serialize(meta.content)}" />`,
124-
)
122+
allMetaString.push(`<meta property="${meta.property}" content="${xss(meta.content)}" />`)
125123
break
126124
}
127125
case "title": {
128126
if (meta.title) {
129-
template = template.replace(`<!-- TITLE -->`, `${serialize(meta.title)} | Follow`)
127+
template = template.replace(`<!-- TITLE -->`, `${xss(meta.title)} | Follow`)
130128
isTitleReplaced = true
131129
}
132130
break
@@ -137,7 +135,7 @@ async function injectMetaToTemplate(template: string, req: FastifyRequest) {
137135
const script = document.createElement("script")
138136
script.innerHTML = `
139137
window.__HYDRATE__ = window.__HYDRATE__ || {}
140-
window.__HYDRATE__['${meta.key}'] = ${serialize(meta.data)}
138+
window.__HYDRATE__['${meta.key}'] = ${xss(JSON.stringify(meta.data))}
141139
`
142140
document.head.append(script)
143141
template = document.toString()

pnpm-lock.yaml

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

0 commit comments

Comments
 (0)