Skip to content

Commit f502161

Browse files
committed
fix: validate npm package names
1 parent 6d54045 commit f502161

10 files changed

Lines changed: 35 additions & 0 deletions

File tree

app/pages/[...package].vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { joinURL } from 'ufo'
33
import type { PackumentVersion, NpmVersionDist } from '#shared/types'
44
import type { JsrPackageInfo } from '#shared/types/jsr'
5+
import { assertValidPackageName } from '#shared/utils/npm'
56
67
definePageMeta({
78
name: 'package',
@@ -37,6 +38,10 @@ const parsedRoute = computed(() => {
3738
const packageName = computed(() => parsedRoute.value.packageName)
3839
const requestedVersion = computed(() => parsedRoute.value.requestedVersion)
3940
41+
if (import.meta.server) {
42+
assertValidPackageName(packageName.value)
43+
}
44+
4045
// Extract org name from scoped package (e.g., "@nuxt/kit" -> "nuxt")
4146
const orgName = computed(() => {
4247
const name = packageName.value

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"shiki": "^3.21.0",
4545
"ufo": "^1.6.3",
4646
"unplugin-vue-router": "^0.19.2",
47+
"validate-npm-package-name": "^7.0.2",
4748
"vue": "3.5.27",
4849
"vue-data-ui": "^3.13.2"
4950
},
@@ -56,6 +57,7 @@
5657
"@types/npmcli__arborist": "^6.3.2",
5758
"@types/sanitize-html": "^2.16.0",
5859
"@types/semver": "^7.7.1",
60+
"@types/validate-npm-package-name": "^4.0.2",
5961
"@unocss/nuxt": "^66.6.0",
6062
"@unocss/preset-wind4": "^66.6.0",
6163
"@vite-pwa/assets-generator": "^1.0.2",

pnpm-lock.yaml

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

server/api/jsr/[...pkg].get.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export default defineCachedEventHandler<Promise<JsrPackageInfo>>(
1414
if (!pkgPath) {
1515
throw createError({ statusCode: 400, message: 'Package name is required' })
1616
}
17+
assertValidPackageName(pkgPath)
1718

1819
return await fetchJsrPackageInfo(pkgPath)
1920
},

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export default defineCachedEventHandler(
66
}
77

88
const packageName = pkg.replace(/\//g, '/')
9+
assertValidPackageName(packageName)
910

1011
try {
1112
return await fetchNpmPackage(packageName)

server/api/registry/file/[...pkg].get.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export default defineCachedEventHandler(
113113
message: 'Package name, version, and file path are required',
114114
})
115115
}
116+
assertValidPackageName(packageName)
116117

117118
try {
118119
const content = await fetchFileContent(packageName, version, filePath)

server/api/registry/files/[...pkg].get.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default defineCachedEventHandler(
2727
if (!packageName || !version) {
2828
throw createError({ statusCode: 400, message: 'Package name and version are required' })
2929
}
30+
assertValidPackageName(packageName)
3031

3132
try {
3233
const jsDelivrData = await fetchFileTree(packageName, version)

server/api/registry/install-size/[...pkg].get.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export default defineCachedEventHandler(
3737
if (!packageName) {
3838
throw createError({ statusCode: 400, message: 'Package name is required' })
3939
}
40+
assertValidPackageName(packageName)
4041

4142
// If no version specified, resolve to latest
4243
let version = requestedVersion

server/api/registry/readme/[...pkg].get.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export default defineCachedEventHandler(
5858
if (!packageName) {
5959
throw createError({ statusCode: 400, message: 'Package name is required' })
6060
}
61+
assertValidPackageName(packageName)
6162

6263
try {
6364
const packageData = await fetchNpmPackage(packageName)

shared/utils/npm.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import validatePackageName from 'validate-npm-package-name'
2+
3+
/**
4+
* Validate an npm package name and throw an HTTP error if invalid.
5+
* Uses validate-npm-package-name to check against npm naming rules.
6+
*/
7+
export function assertValidPackageName(name: string): void {
8+
const result = validatePackageName(name)
9+
if (!result.validForNewPackages && !result.validForOldPackages) {
10+
const errors = [...(result.errors ?? []), ...(result.warnings ?? [])]
11+
throw createError({
12+
statusCode: 400,
13+
message: `Invalid package name: ${errors[0] ?? 'unknown error'}`,
14+
})
15+
}
16+
}

0 commit comments

Comments
 (0)