Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion server/api/registry/badge/[type]/[...pkg].get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ const NPM_DOWNLOADS_API = 'https://api.npmjs.org/downloads/point'
const OSV_QUERY_API = 'https://api.osv.dev/v1/query'
const BUNDLEPHOBIA_API = 'https://bundlephobia.com/api/size'

const SafeStringSchema = v.pipe(v.string(), v.regex(/^[^<>"&]*$/, 'Invalid characters'))
// Allow semver range operators like >= and <= while still rejecting quotes and ampersands.
const SafeStringSchema = v.pipe(v.string(), v.regex(/^[^"&]*$/, 'Invalid characters'))
Comment on lines +16 to +17
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

❓ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Description: Check how the value parameter from SafeStringSchema is used downstream

# Find where SafeStringSchema validation is applied and trace the value usage
rg -n -A 10 "SafeStringSchema" --type=ts

# Look for SVG rendering or badge generation that uses the value parameter
rg -n -B 3 -A 5 "value.*badge|badge.*value" --type=ts -g 'server/api/registry/badge/**'

# Check for any escaping functions or sanitization
rg -n "escape|sanitize|encode" --type=ts -g 'server/api/registry/badge/**'

Repository: npmx-dev/npmx.dev

Length of output: 1912


Review SafeStringSchema badge value sanitisation for XSS/SVG injection
The updated regex in server/api/registry/badge/[type]/[...pkg].get.ts permits < and > while only rejecting " and &. If the validated value is embedded into the badge’s SVG/HTML without strict escaping/encoding, this can enable XSS. Also the pattern is inconsistent in that it blocks double quotes but not single quotes—either block ' too or document why it’s safe.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/api/registry/badge/`[type]/[...pkg].get.ts around lines 16 - 17,
SafeStringSchema currently allows < and > (and allows single quotes) which can
enable XSS/SVG injection when the validated value is embedded into the badge
SVG/HTML; update the validation and/or output encoding: tighten SafeStringSchema
to reject characters that can break XML/HTML contexts (for example ensure it
disallows <, >, &, " and '), or replace the regex with a conservative whitelist
(alphanumeric, dash, underscore, space, max length) and then always
XML-escape/HTML-encode the value before inserting into the SVG template in the
handler that renders the badge (refer to SafeStringSchema and the badge
rendering path that consumes its value). Ensure the sanitizer and the final
output encoding are both in place rather than relying on one or the other.

const SafeColorSchema = v.pipe(
v.string(),
v.transform(value => (value.startsWith('#') ? value : `#${value}`)),
Expand Down
11 changes: 11 additions & 0 deletions test/e2e/badge.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,17 @@ test.describe('badge API', () => {
expect(body).toContain(customValue)
})

test('custom value parameter supports semver range operators', async ({ page, baseURL }) => {
const customValue = '>=22.13.0'
const url = toLocalUrl(
baseURL,
`/api/registry/badge/engines/nuxt?value=${encodeURIComponent(customValue)}`,
)
const { body } = await fetchBadge(page, url)

expect(body).toContain(customValue)
})

test('style=default keeps current badge renderer', async ({ page, baseURL }) => {
const url = toLocalUrl(baseURL, '/api/registry/badge/version/nuxt?style=default')
const { body } = await fetchBadge(page, url)
Expand Down
Loading