cnpmcore
Version:
Private NPM Registry for Enterprise
246 lines • 23.3 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
import { Type } from '@eggjs/typebox-validate/typebox';
import { HTTPContext, Context, HTTPBody, HTTPController, HTTPMethod, HTTPMethodEnum, HTTPParam, Inject } from 'egg';
import { ConflictError, ForbiddenError, UnprocessableEntityError } from 'egg/errors';
import { isEqual } from 'lodash-es';
import { checkData, fromData } from 'ssri';
import validateNpmPackageName from 'validate-npm-package-name';
import { FULLNAME_REG_STRING, extractPackageJSON, getScopeAndName } from "../../../common/PackageUtil.js";
import { Description as DescriptionType, Name as NameType, TagWithVersionRule, VersionRule } from "../../typebox.js";
import { AbstractController } from "../AbstractController.js";
const STRICT_CHECK_TARBALL_FIELDS = [
'name',
'version',
'scripts',
'dependencies',
'devDependencies',
'peerDependencies',
'optionalDependencies',
'license',
'licenses',
'bin',
];
const FullPackageRule = Type.Object({
name: NameType,
// Since we don't validate versions & _attachments previous, here we use Type.Any() just for object validate
versions: Type.Optional(Type.Any()),
_attachments: Type.Optional(Type.Any()),
description: Type.Optional(DescriptionType),
'dist-tags': Type.Optional(Type.Record(Type.String(), Type.String())),
readme: Type.Optional(Type.String({ transform: ['trim'] })),
});
// base64 regex https://stackoverflow.com/questions/475074/regex-to-parse-or-validate-base64-data/475217#475217
const PACKAGE_ATTACH_DATA_RE = /^[A-Za-z0-9+/]{4}/;
let SavePackageVersionController = class SavePackageVersionController extends AbstractController {
// https://github.com/cnpm/cnpmjs.org/blob/master/docs/registry-api.md#publish-a-new-package
// https://github.com/npm/libnpmpublish/blob/main/publish.js#L43
async save(ctx, fullname, pkg) {
this.validateNpmCommand(ctx);
ctx.tValidate(FullPackageRule, pkg);
const { user } = await this.ensurePublishAccess(ctx, fullname, false);
// oxlint-disable-next-line no-param-reassign
fullname = fullname.trim();
if (fullname !== pkg.name) {
throw new UnprocessableEntityError(`fullname(${fullname}) not match package.name(${pkg.name})`);
}
// Using https://github.com/npm/validate-npm-package-name to validate package name
const validateResult = validateNpmPackageName(pkg.name);
if (!validateResult.validForNewPackages) {
// if pkg already exists, still allow to publish
const [scope, name] = getScopeAndName(fullname);
const pkg = await this.packageRepository.findPackage(scope, name);
if (!pkg) {
const errors = (validateResult.errors || validateResult.warnings || []).join(', ');
throw new UnprocessableEntityError(`package.name invalid, errors: ${errors}`);
}
}
const versions = Object.values(pkg.versions);
if (versions.length === 0) {
throw new UnprocessableEntityError('versions is empty');
}
// auth maintainter
const attachments = pkg._attachments ?? {};
const attachmentFilename = Object.keys(attachments)[0];
if (!attachmentFilename) {
// `deprecated: ''` meaning remove deprecated message
const isDeprecatedRequest = versions.some((version) => 'deprecated' in version);
// handle deprecated request
// PUT /:fullname?write=true
// https://github.com/npm/cli/blob/latest/lib/commands/deprecate.js#L48
if (isDeprecatedRequest) {
return await this.saveDeprecatedVersions(pkg.name, versions);
}
// invalid attachments
throw new UnprocessableEntityError('_attachments is empty');
}
// handle add new version
const packageVersion = versions[0];
// check version format
ctx.tValidate(VersionRule, packageVersion);
const attachment = attachments[attachmentFilename];
const distTags = pkg['dist-tags'] ?? {};
let tagNames = Object.keys(distTags);
if (tagNames.length === 0) {
throw new UnprocessableEntityError('dist-tags is empty');
}
const [scope, name] = getScopeAndName(fullname);
// see @https://github.com/cnpm/cnpmcore/issues/574
// add default latest tag
if (!distTags.latest) {
const existsPkg = await this.packageRepository.findPackage(scope, name);
const existsLatestTag = existsPkg && (await this.packageRepository.findPackageTag(existsPkg?.packageId, 'latest'));
if (!existsPkg || !existsLatestTag) {
this.logger.warn('[package:version:add] add default latest tag');
distTags.latest = distTags[tagNames[0]];
tagNames = [...tagNames, 'latest'];
}
}
const tagWithVersion = { tag: tagNames[0], version: distTags[tagNames[0]] };
ctx.tValidate(TagWithVersionRule, tagWithVersion);
if (tagWithVersion.version !== packageVersion.version) {
throw new UnprocessableEntityError(`dist-tags version "${tagWithVersion.version}" not match package version "${packageVersion.version}"`);
}
// check attachment data format and size
if (!attachment.data || typeof attachment.data !== 'string') {
throw new UnprocessableEntityError('attachment.data format invalid');
}
if (!PACKAGE_ATTACH_DATA_RE.test(attachment.data)) {
throw new UnprocessableEntityError('attachment.data string format invalid');
}
const tarballBytes = Buffer.from(attachment.data, 'base64');
if (tarballBytes.length !== attachment.length) {
throw new UnprocessableEntityError(`attachment size ${attachment.length} not match download size ${tarballBytes.length}`);
}
// check integrity or shasum
const integrity = packageVersion.dist?.integrity;
// for content security reason
// check integrity
if (integrity) {
const algorithm = checkData(tarballBytes, integrity);
if (!algorithm) {
throw new UnprocessableEntityError('dist.integrity invalid');
}
}
else {
const integrityObj = fromData(tarballBytes, {
algorithms: ['sha1'],
});
const shasum = integrityObj.sha1[0].hexDigest();
if (packageVersion.dist?.shasum && packageVersion.dist.shasum !== shasum) {
// if integrity not exists, check shasum
throw new UnprocessableEntityError('dist.shasum invalid');
}
}
// https://github.com/cnpm/cnpmcore/issues/542
// check tgz & manifests
if (this.config.cnpmcore.strictValidateTarballPkg) {
const tarballPkg = await extractPackageJSON(tarballBytes);
const versionManifest = pkg.versions[tarballPkg.version];
const diffKeys = STRICT_CHECK_TARBALL_FIELDS.filter((key) => {
const targetKey = key;
return !isEqual(tarballPkg[key], versionManifest[targetKey]);
});
if (diffKeys.length > 0) {
throw new UnprocessableEntityError(`${diffKeys} mismatch between tarball and manifest`);
}
}
// make sure readme is string
const readme = typeof packageVersion.readme === 'string' ? packageVersion.readme : '';
// remove readme
packageVersion.readme = undefined;
// make sure description is string
if (typeof packageVersion.description !== 'string') {
packageVersion.description = '';
}
const registry = await this.registryManagerService.ensureSelfRegistry();
let packageVersionEntity;
const lockName = `${pkg.name}:publish`;
const lockRes = await this.cacheAdapter.usingLock(`${pkg.name}:publish`, 60, async () => {
packageVersionEntity = await this.packageManagerService.publish({
scope,
name,
version: packageVersion.version,
description: packageVersion.description,
packageJson: packageVersion,
readme,
dist: {
content: tarballBytes,
},
tags: tagNames,
registryId: registry.registryId,
isPrivate: true,
}, user);
});
// lock fail
if (!lockRes) {
this.logger.warn('[package:version:add] check lock:%s fail', lockName);
throw new ConflictError('Unable to create the publication lock, please try again later.');
}
this.logger.info('[package:version:add] %s@%s, packageVersionId: %s, tag: %s, userId: %s', packageVersion.name, packageVersion.version, packageVersionEntity?.packageVersionId, tagWithVersion.tag, user?.userId);
ctx.status = 201;
return {
ok: true,
rev: `${packageVersionEntity?.id}-${packageVersionEntity?.packageVersionId}`,
};
}
// https://github.com/cnpm/cnpmjs.org/issues/415
async saveDeprecatedVersions(fullname, versions) {
const pkg = await this.getPackageEntityByFullname(fullname);
await this.packageManagerService.saveDeprecatedVersions(pkg, versions.map((v) => ({ version: v.version, deprecated: v.deprecated })));
return { ok: true };
}
validateNpmCommand(ctx) {
// forbidden star/unstar request
// npm@6: referer: 'star [REDACTED]'
// npm@>=7: 'npm-command': 'star'
let command = ctx.get('npm-command');
if (!command) {
command = ctx.get('referer').split(' ', 1)[0];
}
if (command === 'star' || command === 'unstar') {
throw new ForbiddenError(`npm ${command} is not allowed`);
}
}
};
__decorate([
Inject(),
__metadata("design:type", Function)
], SavePackageVersionController.prototype, "packageManagerService", void 0);
__decorate([
Inject(),
__metadata("design:type", Function)
], SavePackageVersionController.prototype, "registryManagerService", void 0);
__decorate([
Inject(),
__metadata("design:type", Function)
], SavePackageVersionController.prototype, "cacheAdapter", void 0);
__decorate([
HTTPMethod({
// PUT /:fullname
// https://www.npmjs.com/package/path-to-regexp#custom-matching-parameters
path: `/:fullname(${FULLNAME_REG_STRING})`,
method: HTTPMethodEnum.PUT,
}),
__param(0, HTTPContext()),
__param(1, HTTPParam()),
__param(2, HTTPBody()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Context, String, Object]),
__metadata("design:returntype", Promise)
], SavePackageVersionController.prototype, "save", null);
SavePackageVersionController = __decorate([
HTTPController()
], SavePackageVersionController);
export { SavePackageVersionController };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2F2ZVBhY2thZ2VWZXJzaW9uQ29udHJvbGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL2FwcC9wb3J0L2NvbnRyb2xsZXIvcGFja2FnZS9TYXZlUGFja2FnZVZlcnNpb25Db250cm9sbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7OztBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQWUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNwRSxPQUFPLEVBQUUsV0FBVyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsY0FBYyxFQUFFLFVBQVUsRUFBRSxjQUFjLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLEtBQUssQ0FBQztBQUNwSCxPQUFPLEVBQUUsYUFBYSxFQUFFLGNBQWMsRUFBRSx3QkFBd0IsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUNyRixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBRTNDLE9BQU8sc0JBQXNCLE1BQU0sMkJBQTJCLENBQUM7QUFHL0QsT0FBTyxFQUFFLG1CQUFtQixFQUFFLGtCQUFrQixFQUFFLGVBQWUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBSzFHLE9BQU8sRUFBRSxXQUFXLElBQUksZUFBZSxFQUFFLElBQUksSUFBSSxRQUFRLEVBQUUsa0JBQWtCLEVBQUUsV0FBVyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDckgsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFOUQsTUFBTSwyQkFBMkIsR0FBMEI7SUFDekQsTUFBTTtJQUNOLFNBQVM7SUFDVCxTQUFTO0lBQ1QsY0FBYztJQUNkLGlCQUFpQjtJQUNqQixrQkFBa0I7SUFDbEIsc0JBQXNCO0lBQ3RCLFNBQVM7SUFDVCxVQUFVO0lBQ1YsS0FBSztDQUNOLENBQUM7QUFnQkYsTUFBTSxl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