fix(npm): decode URL-encoded package names after regex extraction
Scoped npm packages use %2f encoding for the slash in URLs (e.g. @scope%2fpackage). Previously, the encoded name was used as-is for storage and packument metadata, causing npm install to fail with EINVALIDPACKAGENAME. Now each regex extraction point decodes the package name via decodeURIComponent while keeping the path encoded for correct regex matching.
This commit is contained in:
@@ -155,45 +155,45 @@ export class NpmRegistry extends BaseRegistry {
|
||||
// Dist-tags: /-/package/{package}/dist-tags
|
||||
const distTagsMatch = path.match(/^\/-\/package\/(@?[^\/]+(?:\/[^\/]+)?)\/dist-tags(?:\/(.+))?$/);
|
||||
if (distTagsMatch) {
|
||||
const [, packageName, tag] = distTagsMatch;
|
||||
return this.handleDistTags(context.method, packageName, tag, context.body, token);
|
||||
const [, rawPkgName, tag] = distTagsMatch;
|
||||
return this.handleDistTags(context.method, decodeURIComponent(rawPkgName), tag, context.body, token);
|
||||
}
|
||||
|
||||
// Tarball download: /{package}/-/{filename}.tgz
|
||||
const tarballMatch = path.match(/^\/(@?[^\/]+(?:\/[^\/]+)?)\/-\/(.+\.tgz)$/);
|
||||
if (tarballMatch) {
|
||||
const [, packageName, filename] = tarballMatch;
|
||||
return this.handleTarballDownload(packageName, filename, token, actor);
|
||||
const [, rawPkgName, filename] = tarballMatch;
|
||||
return this.handleTarballDownload(decodeURIComponent(rawPkgName), filename, token, actor);
|
||||
}
|
||||
|
||||
// Unpublish specific version: DELETE /{package}/-/{version}
|
||||
const unpublishVersionMatch = path.match(/^\/(@?[^\/]+(?:\/[^\/]+)?)\/-\/([^\/]+)$/);
|
||||
if (unpublishVersionMatch && context.method === 'DELETE') {
|
||||
const [, packageName, version] = unpublishVersionMatch;
|
||||
this.logger.log('debug', 'unpublishVersionMatch', { packageName, version });
|
||||
return this.unpublishVersion(packageName, version, token);
|
||||
const [, rawPkgName, version] = unpublishVersionMatch;
|
||||
this.logger.log('debug', 'unpublishVersionMatch', { packageName: decodeURIComponent(rawPkgName), version });
|
||||
return this.unpublishVersion(decodeURIComponent(rawPkgName), version, token);
|
||||
}
|
||||
|
||||
// Unpublish entire package: DELETE /{package}/-rev/{rev}
|
||||
const unpublishPackageMatch = path.match(/^\/(@?[^\/]+(?:\/[^\/]+)?)\/-rev\/([^\/]+)$/);
|
||||
if (unpublishPackageMatch && context.method === 'DELETE') {
|
||||
const [, packageName, rev] = unpublishPackageMatch;
|
||||
this.logger.log('debug', 'unpublishPackageMatch', { packageName, rev });
|
||||
return this.unpublishPackage(packageName, token);
|
||||
const [, rawPkgName, rev] = unpublishPackageMatch;
|
||||
this.logger.log('debug', 'unpublishPackageMatch', { packageName: decodeURIComponent(rawPkgName), rev });
|
||||
return this.unpublishPackage(decodeURIComponent(rawPkgName), token);
|
||||
}
|
||||
|
||||
// Package version: /{package}/{version}
|
||||
const versionMatch = path.match(/^\/(@?[^\/]+(?:\/[^\/]+)?)\/([^\/]+)$/);
|
||||
if (versionMatch) {
|
||||
const [, packageName, version] = versionMatch;
|
||||
this.logger.log('debug', 'versionMatch', { packageName, version });
|
||||
return this.handlePackageVersion(packageName, version, token, actor);
|
||||
const [, rawPkgName, version] = versionMatch;
|
||||
this.logger.log('debug', 'versionMatch', { packageName: decodeURIComponent(rawPkgName), version });
|
||||
return this.handlePackageVersion(decodeURIComponent(rawPkgName), version, token, actor);
|
||||
}
|
||||
|
||||
// Package operations: /{package}
|
||||
const packageMatch = path.match(/^\/(@?[^\/]+(?:\/[^\/]+)?)$/);
|
||||
if (packageMatch) {
|
||||
const packageName = packageMatch[1];
|
||||
const packageName = decodeURIComponent(packageMatch[1]);
|
||||
this.logger.log('debug', 'packageMatch', { packageName });
|
||||
return this.handlePackage(context.method, packageName, context.body, context.query, token, actor);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user