cnpm / cnpmjs.org

@@ -1717,11 +1717,13 @@
Loading
1717 1717
      mod.package._publish_on_cnpm = true;
1718 1718
    }
1719 1719
1720 -
    var dist = {
1720 +
    // keep orgin dist fields, like `integrity` and so on
1721 +
    var dist = Object.assign({}, sourcePackage.dist, {
1722 +
      tarball: '',
1721 1723
      shasum: shasum,
1722 1724
      size: dataSize,
1723 1725
      noattachment: dataSize === 0,
1724 -
    };
1726 +
    });
1725 1727
1726 1728
    if (result.url) {
1727 1729
      dist.tarball = result.url;

@@ -1,9 +1,10 @@
Loading
1 1
'use strict';
2 2
3 3
var debug = require('debug')('cnpmjs.org:controllers:registry:package:save');
4 -
var crypto = require('crypto');
4 +
var ssri = require('ssri');
5 5
var deprecateVersions = require('./deprecate');
6 6
var packageService = require('../../../services/package');
7 +
var logger = require('../../../common/logger');
7 8
var common = require('../../../lib/common');
8 9
var nfs = require('../../../common/nfs');
9 10
var config = require('../../../config');
@@ -16,14 +17,42 @@
Loading
16 17
//
17 18
// new flows: only one request
18 19
// PUT /:name
19 -
// https://github.com/npm/npm-registry-client/blob/master/lib/publish.js#L84
20 +
// old publish: https://github.com/npm/npm-registry-client/blob/master/lib/publish.js#L84
21 +
// new publish: https://github.com/npm/libnpmpublish/blob/main/publish.js#L91
20 22
module.exports = function* save(next) {
21 -
  // 'dist-tags': { latest: '0.0.2' },
22 -
  //  _attachments:
23 -
  // { 'nae-sandbox-0.0.2.tgz':
24 -
  //    { content_type: 'application/octet-stream',
25 -
  //      data: 'H4sIAAAAA
26 -
  //      length: 9883
23 +
  // {
24 +
  //   "_id": "@cnpm/foo",
25 +
  //   "name": "@cnpm/foo",
26 +
  //   "dist-tags": {
27 +
  //     "latest": "1.0.0"
28 +
  //   },
29 +
  //   "versions": {
30 +
  //     "1.0.0": {
31 +
  //       "name": "@cnpm/foo",
32 +
  //       "version": "1.0.0",
33 +
  //       "dependencies": {
34 +
  //         "xprofiler": "^1.2.6"
35 +
  //       },
36 +
  //       "readme": "ERROR: No README data found!",
37 +
  //       "_id": "@cnpm/foo@1.0.0",
38 +
  //       "_nodeVersion": "16.13.0",
39 +
  //       "_npmVersion": "8.1.0",
40 +
  //       "dist": {
41 +
  //         "integrity": "sha512-7nm0vpDEWs7y+tTwlxd7YnGaBc+9Gk5KaPsx2cqQz6H84ndBXlw5nMxGtL4Uy0bCQIknPAZAVe+KNheInmmJrQ==",
42 +
  //         "shasum": "afd05dcfb8759b9b1c7151492a04f2254365c602",
43 +
  //         "tarball": "http://127.0.0.1:7001/@cnpm/foo/-/@cnpm/foo-1.0.0.tgz"
44 +
  //       }
45 +
  //     }
46 +
  //   },
47 +
  //   "access": null,
48 +
  //   "_attachments": {
49 +
  //     "@cnpm/foo-1.0.0.tgz": {
50 +
  //       "content_type": "application/octet-stream",
51 +
  //       "data": "H4sIAAAAAA...",
52 +
  //       "length": 208
53 +
  //     }
54 +
  //   }
55 +
  // }
27 56
  var pkg = this.request.body;
28 57
  var username = this.user.name;
29 58
  var name = this.params.name || this.params[0];
@@ -150,7 +179,6 @@
Loading
150 179
    username, name, version, attachment.length, versionPackage.maintainers, distTags);
151 180
152 181
  var exists = yield packageService.getModule(name, version);
153 -
  var shasum;
154 182
  if (exists) {
155 183
    this.status = 403;
156 184
    const error = '[forbidden] cannot modify pre-existing version: ' + version;
@@ -186,21 +214,60 @@
Loading
186 214
    }
187 215
  }
188 216
189 -
  shasum = crypto.createHash('sha1');
190 -
  shasum.update(tarballBuffer);
191 -
  shasum = shasum.digest('hex');
217 +
  var originDist = versionPackage.dist || {};
218 +
  var shasum;
219 +
  var integrity = originDist.integrity;
220 +
  // for content security reason
221 +
  // check integrity
222 +
  if (integrity) {
223 +
    var algorithm = ssri.checkData(tarballBuffer, integrity);
224 +
    if (!algorithm) {
225 +
      logger.error('[registry:save:integrity:invalid] %s@%s, dist:%j', name, version, originDist);
226 +
      this.status = 400;
227 +
      const error = '[invalid] dist.integrity invalid';
228 +
      this.body = {
229 +
        error,
230 +
        reason: error,
231 +
      };
232 +
      return;
233 +
    }
234 +
    var integrityObj = ssri.fromData(tarballBuffer, {
235 +
      algorithms: ['sha1'],
236 +
    });
237 +
    shasum = integrityObj.sha1[0].hexDigest();
238 +
  } else {
239 +
    var integrityObj = ssri.fromData(tarballBuffer, {
240 +
      algorithms: ['sha512', 'sha1'],
241 +
    });
242 +
    integrity = integrityObj.sha512[0].toString();
243 +
    shasum = integrityObj.sha1[0].hexDigest();
244 +
    if (originDist.shasum && originDist.shasum !== shasum) {
245 +
      // if integrity not exists, check shasum
246 +
      logger.error('[registry:save:shasum:invalid] %s@%s, dist:%j', name, version, originDist);
247 +
      this.status = 400;
248 +
      const error = '[invalid] dist.shasum invalid';
249 +
      this.body = {
250 +
        error,
251 +
        reason: error,
252 +
      };
253 +
      return;
254 +
    }
255 +
  }
192 256
193 257
  var options = {
194 258
    key: common.getCDNKey(name, filename),
195 -
    shasum: shasum
259 +
    shasum: shasum,
260 +
    integrity: integrity,
196 261
  };
197 262
  var uploadResult = yield nfs.uploadBuffer(tarballBuffer, options);
198 -
  debug('upload %j', uploadResult);
263 +
  debug('upload %j, options: %j', uploadResult, options);
199 264
200 -
  var dist = {
265 +
  var dist = Object.assign({}, originDist, {
266 +
    tarball: '',
267 +
    integrity: integrity,
201 268
    shasum: shasum,
202 -
    size: attachment.length
203 -
  };
269 +
    size: attachment.length,
270 +
  });
204 271
205 272
  // if nfs upload return a key, record it
206 273
  if (uploadResult.url) {
Files Coverage
common 67.39%
controllers 86.46%
middleware 94.34%
models 90.42%
routes 100.00%
servers 90.91%
services 90.86%
sync 89.61%
config/index.js 75.51%
lib/common.js 89.19%
Project Totals (97 files) 87.90%
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading