Skip to content

Commit 577c10e

Browse files
authored
chore: add a little extra validation (#1894)
1 parent 4d70dd8 commit 577c10e

File tree

2 files changed

+33
-9
lines changed

2 files changed

+33
-9
lines changed

server/api/registry/badge/[type]/[...pkg].get.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ function measureDefaultTextWidth(text: string): number {
8787
return Math.max(MIN_BADGE_TEXT_WIDTH, Math.round(text.length * CHAR_WIDTH) + BADGE_PADDING_X * 2)
8888
}
8989

90+
function escapeXML(str: string): string {
91+
return str
92+
.replace(/&/g, '&')
93+
.replace(/</g, '&lt;')
94+
.replace(/>/g, '&gt;')
95+
.replace(/"/g, '&quot;')
96+
}
97+
9098
function measureShieldsTextLength(text: string): number {
9199
const measuredWidth = measureTextWidth(text, SHIELDS_FONT_SHORTHAND)
92100

@@ -108,9 +116,11 @@ function renderDefaultBadgeSvg(params: {
108116
const rightWidth = measureDefaultTextWidth(finalValue)
109117
const totalWidth = leftWidth + rightWidth
110118
const height = 20
119+
const escapedLabel = escapeXML(finalLabel)
120+
const escapedValue = escapeXML(finalValue)
111121

112122
return `
113-
<svg xmlns="http://www.w3.org/2000/svg" width="${totalWidth}" height="${height}" role="img" aria-label="${finalLabel}: ${finalValue}">
123+
<svg xmlns="http://www.w3.org/2000/svg" width="${totalWidth}" height="${height}" role="img" aria-label="${escapedLabel}: ${escapedValue}">
114124
<clipPath id="r">
115125
<rect width="${totalWidth}" height="${height}" rx="3" fill="#fff"/>
116126
</clipPath>
@@ -119,8 +129,8 @@ function renderDefaultBadgeSvg(params: {
119129
<rect x="${leftWidth}" width="${rightWidth}" height="${height}" fill="${finalColor}"/>
120130
</g>
121131
<g text-anchor="middle" font-family="Geist, system-ui, -apple-system, sans-serif" font-size="11">
122-
<text x="${leftWidth / 2}" y="14" fill="#ffffff">${finalLabel}</text>
123-
<text x="${leftWidth + rightWidth / 2}" y="14" fill="#ffffff">${finalValue}</text>
132+
<text x="${leftWidth / 2}" y="14" fill="#ffffff">${escapedLabel}</text>
133+
<text x="${leftWidth + rightWidth / 2}" y="14" fill="#ffffff">${escapedValue}</text>
124134
</g>
125135
</svg>
126136
`.trim()
@@ -141,7 +151,9 @@ function renderShieldsBadgeSvg(params: {
141151
const rightWidth = rightTextLength + SHIELDS_LABEL_PADDING_X * 2
142152
const totalWidth = leftWidth + rightWidth
143153
const height = 20
144-
const title = `${finalLabel}: ${finalValue}`
154+
const escapedLabel = escapeXML(finalLabel)
155+
const escapedValue = escapeXML(finalValue)
156+
const title = `${escapedLabel}: ${escapedValue}`
145157

146158
const leftCenter = Math.round((leftWidth / 2) * 10)
147159
const rightCenter = Math.round((leftWidth + rightWidth / 2) * 10)
@@ -163,10 +175,10 @@ function renderShieldsBadgeSvg(params: {
163175
<rect width="${totalWidth}" height="${height}" fill="url(#s)"/>
164176
</g>
165177
<g fill="#fff" text-anchor="middle" font-family="Verdana, Geneva, DejaVu Sans, sans-serif" text-rendering="geometricPrecision" font-size="110">
166-
<text aria-hidden="true" x="${leftCenter}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="${leftTextLengthAttr}">${finalLabel}</text>
167-
<text x="${leftCenter}" y="140" transform="scale(.1)" fill="#fff" textLength="${leftTextLengthAttr}">${finalLabel}</text>
168-
<text aria-hidden="true" x="${rightCenter}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="${rightTextLengthAttr}">${finalValue}</text>
169-
<text x="${rightCenter}" y="140" transform="scale(.1)" fill="#fff" textLength="${rightTextLengthAttr}">${finalValue}</text>
178+
<text aria-hidden="true" x="${leftCenter}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="${leftTextLengthAttr}">${escapedLabel}</text>
179+
<text x="${leftCenter}" y="140" transform="scale(.1)" fill="#fff" textLength="${leftTextLengthAttr}">${escapedLabel}</text>
180+
<text aria-hidden="true" x="${rightCenter}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="${rightTextLengthAttr}">${escapedValue}</text>
181+
<text x="${rightCenter}" y="140" transform="scale(.1)" fill="#fff" textLength="${rightTextLengthAttr}">${escapedValue}</text>
170182
</g>
171183
</svg>
172184
`.trim()

shared/schemas/social.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,19 @@ export type PackageLikeBody = v.InferOutput<typeof PackageLikeBodySchema>
1313
// TODO: add 'avatar'
1414
export const ProfileEditBodySchema = v.object({
1515
displayName: v.pipe(v.string(), v.maxLength(640)),
16-
website: v.optional(v.union([v.literal(''), v.pipe(v.string(), v.url())])),
16+
website: v.optional(
17+
v.union([
18+
v.literal(''),
19+
v.pipe(
20+
v.string(),
21+
v.url(),
22+
v.check(
23+
url => url.startsWith('https://') || url.startsWith('http://'),
24+
'Website must use http or https',
25+
),
26+
),
27+
]),
28+
),
1729
description: v.optional(v.pipe(v.string(), v.maxLength(2560))),
1830
})
1931

0 commit comments

Comments
 (0)