@verdaccio/middleware 6.0.0-6-next.34 → 6.0.0-6-next.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.babelrc +12 -1
  2. package/CHANGELOG.md +13 -0
  3. package/build/index.d.ts +12 -1
  4. package/build/index.js +130 -4
  5. package/build/index.js.map +1 -1
  6. package/build/middlewares/allow.d.ts +1 -0
  7. package/build/middlewares/allow.js +39 -0
  8. package/build/middlewares/allow.js.map +1 -0
  9. package/build/middlewares/antiLoop.d.ts +7 -0
  10. package/build/middlewares/antiLoop.js +35 -0
  11. package/build/middlewares/antiLoop.js.map +1 -0
  12. package/build/middlewares/encode-pkg.d.ts +8 -0
  13. package/build/middlewares/encode-pkg.js +20 -0
  14. package/build/middlewares/encode-pkg.js.map +1 -0
  15. package/build/middlewares/error.d.ts +4 -0
  16. package/build/middlewares/error.js +75 -0
  17. package/build/middlewares/error.js.map +1 -0
  18. package/build/middlewares/final.d.ts +4 -0
  19. package/build/middlewares/final.js +52 -0
  20. package/build/middlewares/final.js.map +1 -0
  21. package/build/middlewares/json.d.ts +2 -0
  22. package/build/middlewares/json.js +15 -0
  23. package/build/middlewares/json.js.map +1 -0
  24. package/build/middlewares/log.d.ts +5 -0
  25. package/build/middlewares/log.js +101 -0
  26. package/build/middlewares/log.js.map +1 -0
  27. package/build/middlewares/match.d.ts +1 -0
  28. package/build/middlewares/match.js +16 -0
  29. package/build/middlewares/match.js.map +1 -0
  30. package/build/middlewares/media.d.ts +1 -0
  31. package/build/middlewares/media.js +17 -0
  32. package/build/middlewares/media.js.map +1 -0
  33. package/build/middlewares/security-headers.d.ts +2 -0
  34. package/build/middlewares/security-headers.js +21 -0
  35. package/build/middlewares/security-headers.js.map +1 -0
  36. package/build/middlewares/validation.d.ts +3 -0
  37. package/build/middlewares/validation.js +30 -0
  38. package/build/middlewares/validation.js.map +1 -0
  39. package/build/types.d.ts +13 -0
  40. package/build/types.js +6 -0
  41. package/build/types.js.map +1 -0
  42. package/jest.config.js +10 -1
  43. package/package.json +11 -7
  44. package/src/index.ts +17 -1
  45. package/src/middlewares/allow.ts +40 -0
  46. package/src/middlewares/antiLoop.ts +30 -0
  47. package/src/middlewares/encode-pkg.ts +19 -0
  48. package/src/middlewares/error.ts +78 -0
  49. package/src/middlewares/final.ts +60 -0
  50. package/src/middlewares/json.ts +15 -0
  51. package/src/middlewares/log.ts +103 -0
  52. package/src/middlewares/match.ts +16 -0
  53. package/src/middlewares/media.ts +18 -0
  54. package/src/middlewares/security-headers.ts +21 -0
  55. package/src/middlewares/validation.ts +41 -0
  56. package/src/types.ts +11 -0
  57. package/test/allow.spec.ts +82 -0
  58. package/test/encode.spec.ts +21 -0
  59. package/test/final.spec.ts +60 -0
  60. package/test/helper.ts +14 -0
  61. package/test/json.spec.ts +32 -0
  62. package/test/log.spec.ts +28 -0
  63. package/test/loop.spec.ts +31 -0
  64. package/test/media.spec.ts +33 -0
  65. package/test/params.spec.ts +83 -0
  66. package/test/security.spec.ts +54 -0
  67. package/build/middleware-utils.d.ts +0 -6
  68. package/build/middleware-utils.js +0 -16
  69. package/build/middleware-utils.js.map +0 -1
  70. package/build/middleware.d.ts +0 -32
  71. package/build/middleware.js +0 -332
  72. package/build/middleware.js.map +0 -1
  73. package/src/middleware-utils.ts +0 -10
  74. package/src/middleware.ts +0 -413
  75. package/test/middleware-utils.spec.ts +0 -18
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","names":["LOG_STATUS_MESSAGE","LOG_VERDACCIO_ERROR","LOG_VERDACCIO_BYTES","log","req","res","next","logger","child","sub","_auth","headers","authorization","_","isNil","_cookie","get","cookie","url","originalUrl","info","ip","bytesin","on","chunk","length","bytesout","_write","write","buf","apply","arguments","forwardedFor","remoteAddress","connection","remoteIP","message","locals","_verdaccio_error","http","request","method","user","remote_user","name","status","statusCode","error","bytes","in","out","_end","end"],"sources":["../../src/middlewares/log.ts"],"sourcesContent":["import _ from 'lodash';\n\nimport { logger } from '@verdaccio/logger';\n\nimport { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';\n\n// FIXME: deprecated, moved to @verdaccio/dev-commons\nexport const LOG_STATUS_MESSAGE =\n \"@{status}, user: @{user}(@{remoteIP}), req: '@{request.method} @{request.url}'\";\nexport const LOG_VERDACCIO_ERROR = `${LOG_STATUS_MESSAGE}, error: @{!error}`;\nexport const LOG_VERDACCIO_BYTES = `${LOG_STATUS_MESSAGE}, bytes: @{bytes.in}/@{bytes.out}`;\n\nexport function log(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {\n // logger\n req.log = logger.child({ sub: 'in' });\n\n const _auth = req.headers.authorization;\n if (_.isNil(_auth) === false) {\n req.headers.authorization = '<Classified>';\n }\n\n const _cookie = req.get('cookie');\n if (_.isNil(_cookie) === false) {\n req.headers.cookie = '<Classified>';\n }\n\n req.url = req.originalUrl;\n req.log.info({ req: req, ip: req.ip }, \"@{ip} requested '@{req.method} @{req.url}'\");\n req.originalUrl = req.url;\n\n if (_.isNil(_auth) === false) {\n req.headers.authorization = _auth;\n }\n\n if (_.isNil(_cookie) === false) {\n req.headers.cookie = _cookie;\n }\n\n let bytesin = 0;\n req.on('data', function (chunk): void {\n bytesin += chunk.length;\n });\n\n let bytesout = 0;\n const _write = res.write;\n // FIXME: res.write should return boolean\n // @ts-ignore\n res.write = function (buf): boolean {\n bytesout += buf.length;\n /* eslint prefer-rest-params: \"off\" */\n // @ts-ignore\n _write.apply(res, arguments);\n };\n\n const log = function (): void {\n const forwardedFor = req.get('x-forwarded-for');\n const remoteAddress = req.connection.remoteAddress;\n const remoteIP = forwardedFor ? `${forwardedFor} via ${remoteAddress}` : remoteAddress;\n let message;\n if (res.locals._verdaccio_error) {\n message = LOG_VERDACCIO_ERROR;\n } else {\n message = LOG_VERDACCIO_BYTES;\n }\n\n req.url = req.originalUrl;\n req.log.http(\n {\n request: {\n method: req.method,\n url: req.url,\n },\n user: req.remote_user?.name || null,\n remoteIP,\n status: res.statusCode,\n error: res.locals._verdaccio_error,\n bytes: {\n in: bytesin,\n out: bytesout,\n },\n },\n message\n );\n req.originalUrl = req.url;\n };\n\n req.on('close', function (): void {\n log();\n });\n\n const _end = res.end;\n // @ts-ignore\n res.end = function (buf): void {\n if (buf) {\n bytesout += buf.length;\n }\n /* eslint prefer-rest-params: \"off\" */\n // @ts-ignore\n _end.apply(res, arguments);\n log();\n };\n next();\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function match(regexp: RegExp): any;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.match = match;
7
+ function match(regexp) {
8
+ return function (req, res, next, value) {
9
+ if (regexp.exec(value)) {
10
+ next();
11
+ } else {
12
+ next('route');
13
+ }
14
+ };
15
+ }
16
+ //# sourceMappingURL=match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match.js","names":["match","regexp","req","res","next","value","exec"],"sources":["../../src/middlewares/match.ts"],"sourcesContent":["import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';\n\nexport function match(regexp: RegExp): any {\n return function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer,\n value: string\n ): void {\n if (regexp.exec(value)) {\n next();\n } else {\n next('route');\n }\n };\n}\n"],"mappings":";;;;;;AAEO,SAASA,KAAK,CAACC,MAAc,EAAO;EACzC,OAAO,UACLC,GAAmB,EACnBC,GAAoB,EACpBC,IAAsB,EACtBC,KAAa,EACP;IACN,IAAIJ,MAAM,CAACK,IAAI,CAACD,KAAK,CAAC,EAAE;MACtBD,IAAI,EAAE;IACR,CAAC,MAAM;MACLA,IAAI,CAAC,OAAO,CAAC;IACf;EACF,CAAC;AACH"}
@@ -0,0 +1 @@
1
+ export declare function media(expect: string | null): any;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.media = media;
7
+ var _core = require("@verdaccio/core");
8
+ function media(expect) {
9
+ return function (req, res, next) {
10
+ if (req.headers[_core.HEADER_TYPE.CONTENT_TYPE] !== expect) {
11
+ next(_core.errorUtils.getCode(_core.HTTP_STATUS.UNSUPPORTED_MEDIA, 'wrong content-type, expect: ' + expect + ', got: ' + req.get[_core.HEADER_TYPE.CONTENT_TYPE]));
12
+ } else {
13
+ next();
14
+ }
15
+ };
16
+ }
17
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","names":["media","expect","req","res","next","headers","HEADER_TYPE","CONTENT_TYPE","errorUtils","getCode","HTTP_STATUS","UNSUPPORTED_MEDIA","get"],"sources":["../../src/middlewares/media.ts"],"sourcesContent":["import { HEADER_TYPE, HTTP_STATUS, errorUtils } from '@verdaccio/core';\n\nimport { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';\n\nexport function media(expect: string | null): any {\n return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {\n if (req.headers[HEADER_TYPE.CONTENT_TYPE] !== expect) {\n next(\n errorUtils.getCode(\n HTTP_STATUS.UNSUPPORTED_MEDIA,\n 'wrong content-type, expect: ' + expect + ', got: ' + req.get[HEADER_TYPE.CONTENT_TYPE]\n )\n );\n } else {\n next();\n }\n };\n}\n"],"mappings":";;;;;;AAAA;AAIO,SAASA,KAAK,CAACC,MAAqB,EAAO;EAChD,OAAO,UAAUC,GAAmB,EAAEC,GAAoB,EAAEC,IAAsB,EAAQ;IACxF,IAAIF,GAAG,CAACG,OAAO,CAACC,iBAAW,CAACC,YAAY,CAAC,KAAKN,MAAM,EAAE;MACpDG,IAAI,CACFI,gBAAU,CAACC,OAAO,CAChBC,iBAAW,CAACC,iBAAiB,EAC7B,8BAA8B,GAAGV,MAAM,GAAG,SAAS,GAAGC,GAAG,CAACU,GAAG,CAACN,iBAAW,CAACC,YAAY,CAAC,CACxF,CACF;IACH,CAAC,MAAM;MACLH,IAAI,EAAE;IACR;EACF,CAAC;AACH"}
@@ -0,0 +1,2 @@
1
+ import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
2
+ export declare function setSecurityWebHeaders(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.setSecurityWebHeaders = setSecurityWebHeaders;
7
+ var _core = require("@verdaccio/core");
8
+ // TODO: remove, was relocated to web package
9
+ // @ts-deprecated
10
+ function setSecurityWebHeaders(req, res, next) {
11
+ // disable loading in frames (clickjacking, etc.)
12
+ res.header(_core.HEADERS.FRAMES_OPTIONS, 'deny');
13
+ // avoid stablish connections outside of domain
14
+ res.header(_core.HEADERS.CSP, "connect-src 'self'");
15
+ // https://stackoverflow.com/questions/18337630/what-is-x-content-type-options-nosniff
16
+ res.header(_core.HEADERS.CTO, 'nosniff');
17
+ // https://stackoverflow.com/questions/9090577/what-is-the-http-header-x-xss-protection
18
+ res.header(_core.HEADERS.XSS, '1; mode=block');
19
+ next();
20
+ }
21
+ //# sourceMappingURL=security-headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-headers.js","names":["setSecurityWebHeaders","req","res","next","header","HEADERS","FRAMES_OPTIONS","CSP","CTO","XSS"],"sources":["../../src/middlewares/security-headers.ts"],"sourcesContent":["import { HEADERS } from '@verdaccio/core';\n\nimport { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';\n\n// TODO: remove, was relocated to web package\n// @ts-deprecated\nexport function setSecurityWebHeaders(\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n): void {\n // disable loading in frames (clickjacking, etc.)\n res.header(HEADERS.FRAMES_OPTIONS, 'deny');\n // avoid stablish connections outside of domain\n res.header(HEADERS.CSP, \"connect-src 'self'\");\n // https://stackoverflow.com/questions/18337630/what-is-x-content-type-options-nosniff\n res.header(HEADERS.CTO, 'nosniff');\n // https://stackoverflow.com/questions/9090577/what-is-the-http-header-x-xss-protection\n res.header(HEADERS.XSS, '1; mode=block');\n next();\n}\n"],"mappings":";;;;;;AAAA;AAIA;AACA;AACO,SAASA,qBAAqB,CACnCC,GAAmB,EACnBC,GAAoB,EACpBC,IAAsB,EAChB;EACN;EACAD,GAAG,CAACE,MAAM,CAACC,aAAO,CAACC,cAAc,EAAE,MAAM,CAAC;EAC1C;EACAJ,GAAG,CAACE,MAAM,CAACC,aAAO,CAACE,GAAG,EAAE,oBAAoB,CAAC;EAC7C;EACAL,GAAG,CAACE,MAAM,CAACC,aAAO,CAACG,GAAG,EAAE,SAAS,CAAC;EAClC;EACAN,GAAG,CAACE,MAAM,CAACC,aAAO,CAACI,GAAG,EAAE,eAAe,CAAC;EACxCN,IAAI,EAAE;AACR"}
@@ -0,0 +1,3 @@
1
+ import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
2
+ export declare function validateName(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer, value: string, name: string): void;
3
+ export declare function validatePackage(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer, value: string, name: string): void;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.validateName = validateName;
7
+ exports.validatePackage = validatePackage;
8
+ var _core = require("@verdaccio/core");
9
+ var _utils = require("@verdaccio/utils");
10
+ function validateName(req, res, next, value, name) {
11
+ if (value === '-') {
12
+ // special case in couchdb usually
13
+ next('route');
14
+ } else if ((0, _utils.validateName)(value)) {
15
+ next();
16
+ } else {
17
+ next(_core.errorUtils.getForbidden('invalid ' + name));
18
+ }
19
+ }
20
+ function validatePackage(req, res, next, value, name) {
21
+ if (value === '-') {
22
+ // special case in couchdb usually
23
+ next('route');
24
+ } else if ((0, _utils.validatePackage)(value)) {
25
+ next();
26
+ } else {
27
+ next(_core.errorUtils.getForbidden('invalid ' + name));
28
+ }
29
+ }
30
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","names":["validateName","req","res","next","value","name","utilValidateName","errorUtils","getForbidden","validatePackage","utilValidatePackage"],"sources":["../../src/middlewares/validation.ts"],"sourcesContent":["import { errorUtils } from '@verdaccio/core';\nimport {\n validateName as utilValidateName,\n validatePackage as utilValidatePackage,\n} from '@verdaccio/utils';\n\nimport { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';\n\nexport function validateName(\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer,\n value: string,\n name: string\n): void {\n if (value === '-') {\n // special case in couchdb usually\n next('route');\n } else if (utilValidateName(value)) {\n next();\n } else {\n next(errorUtils.getForbidden('invalid ' + name));\n }\n}\n\nexport function validatePackage(\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer,\n value: string,\n name: string\n): void {\n if (value === '-') {\n // special case in couchdb usually\n next('route');\n } else if (utilValidatePackage(value)) {\n next();\n } else {\n next(errorUtils.getForbidden('invalid ' + name));\n }\n}\n"],"mappings":";;;;;;;AAAA;AACA;AAOO,SAASA,YAAY,CAC1BC,GAAmB,EACnBC,GAAoB,EACpBC,IAAsB,EACtBC,KAAa,EACbC,IAAY,EACN;EACN,IAAID,KAAK,KAAK,GAAG,EAAE;IACjB;IACAD,IAAI,CAAC,OAAO,CAAC;EACf,CAAC,MAAM,IAAI,IAAAG,mBAAgB,EAACF,KAAK,CAAC,EAAE;IAClCD,IAAI,EAAE;EACR,CAAC,MAAM;IACLA,IAAI,CAACI,gBAAU,CAACC,YAAY,CAAC,UAAU,GAAGH,IAAI,CAAC,CAAC;EAClD;AACF;AAEO,SAASI,eAAe,CAC7BR,GAAmB,EACnBC,GAAoB,EACpBC,IAAsB,EACtBC,KAAa,EACbC,IAAY,EACN;EACN,IAAID,KAAK,KAAK,GAAG,EAAE;IACjB;IACAD,IAAI,CAAC,OAAO,CAAC;EACf,CAAC,MAAM,IAAI,IAAAO,sBAAmB,EAACN,KAAK,CAAC,EAAE;IACrCD,IAAI,EAAE;EACR,CAAC,MAAM;IACLA,IAAI,CAACI,gBAAU,CAACC,YAAY,CAAC,UAAU,GAAGH,IAAI,CAAC,CAAC;EAClD;AACF"}
@@ -0,0 +1,13 @@
1
+ import { NextFunction, Request, Response } from 'express';
2
+ import { Logger, RemoteUser } from '@verdaccio/types';
3
+ export type $RequestExtend = Request & {
4
+ remote_user?: RemoteUser;
5
+ log: Logger;
6
+ };
7
+ export type $ResponseExtend = Response & {
8
+ cookies?: any;
9
+ };
10
+ export type $NextFunctionVer = NextFunction & any;
11
+ export interface MiddlewareError {
12
+ error: string;
13
+ }
package/build/types.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["import { NextFunction, Request, Response } from 'express';\n\nimport { Logger, RemoteUser } from '@verdaccio/types';\n\nexport type $RequestExtend = Request & { remote_user?: RemoteUser; log: Logger };\nexport type $ResponseExtend = Response & { cookies?: any };\nexport type $NextFunctionVer = NextFunction & any;\n\nexport interface MiddlewareError {\n error: string;\n}\n"],"mappings":""}
package/jest.config.js CHANGED
@@ -1,3 +1,12 @@
1
1
  const config = require('../../jest/config');
2
2
 
3
- module.exports = Object.assign({}, config, {});
3
+ module.exports = Object.assign({}, config, {
4
+ coverageThreshold: {
5
+ global: {
6
+ lines: 67,
7
+ functions: 80,
8
+ branches: 56,
9
+ statements: 67,
10
+ },
11
+ },
12
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verdaccio/middleware",
3
- "version": "6.0.0-6-next.34",
3
+ "version": "6.0.0-6-next.35",
4
4
  "description": "loaders logic",
5
5
  "main": "./build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -30,24 +30,28 @@
30
30
  "npm": ">=6"
31
31
  },
32
32
  "dependencies": {
33
+ "@verdaccio/core": "6.0.0-6-next.56",
34
+ "@verdaccio/logger": "6.0.0-6-next.24",
35
+ "@verdaccio/utils": "6.0.0-6-next.24",
33
36
  "debug": "4.3.4",
34
- "body-parser": "1.20.1",
35
- "@verdaccio/auth": "6.0.0-6-next.34",
36
- "@verdaccio/core": "6.0.0-6-next.55",
37
- "@verdaccio/logger": "6.0.0-6-next.23",
38
- "@verdaccio/utils": "6.0.0-6-next.23",
39
- "lodash": "4.17.21"
37
+ "lodash": "4.17.21",
38
+ "mime": "2.6.0"
40
39
  },
41
40
  "funding": {
42
41
  "type": "opencollective",
43
42
  "url": "https://opencollective.com/verdaccio"
44
43
  },
44
+ "devDependencies": {
45
+ "body-parser": "1.20.1",
46
+ "supertest": "6.3.3"
47
+ },
45
48
  "scripts": {
46
49
  "clean": "rimraf ./build",
47
50
  "type-check": "tsc --noEmit -p tsconfig.build.json",
48
51
  "build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json",
49
52
  "build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
50
53
  "watch": "pnpm build:js -- --watch",
54
+ "test": "jest",
51
55
  "build": "pnpm run build:js && pnpm run build:types"
52
56
  },
53
57
  "readme": "# @verdaccio/middleware\n\n[![backers](https://opencollective.com/verdaccio/tiers/backer/badge.svg?label=Backer&color=brightgreen)](https://opencollective.com/verdaccio)\n[![stackshare](https://img.shields.io/badge/Follow%20on-StackShare-blue.svg?logo=stackshare&style=flat)](https://stackshare.io/verdaccio)\n[![MIT](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)\n[![Crowdin](https://d322cqt584bo4o.cloudfront.net/verdaccio/localized.svg)](https://crowdin.com/project/verdaccio)\n[![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/verdaccio/verdaccio)](https://www.tickgit.com/browse?repo=github.com/verdaccio/verdaccio)\n\n[![Twitter followers](https://img.shields.io/twitter/follow/verdaccio_npm.svg?style=social&label=Follow)](https://twitter.com/verdaccio_npm)\n[![Github](https://img.shields.io/github/stars/verdaccio/verdaccio.svg?style=social&label=Stars)](https://github.com/verdaccio/verdaccio/stargazers)\n\n## Donations\n\nVerdaccio is run by **volunteers**; nobody is working full-time on it. If you find this project to be useful and would like to support its development, consider making a donation - **your logo might end up in this readme.** 😉\n\n**[Donate](https://opencollective.com/verdaccio)** 💵👍🏻 starting from _\\$1/month_ or just one single contribution.\n\n## Report a vulnerability\n\nIf you want to report a security vulnerability, please follow the steps which we have defined for you in our [security policy](https://github.com/verdaccio/verdaccio/security/policy).\n\n## Open Collective Sponsors\n\nSupport this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/verdaccio#sponsor)]\n\n[![sponsor](https://opencollective.com/verdaccio/sponsor/0/avatar.svg)](https://opencollective.com/verdaccio/sponsor/0/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/1/avatar.svg)](https://opencollective.com/verdaccio/sponsor/1/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/2/avatar.svg)](https://opencollective.com/verdaccio/sponsor/2/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/3/avatar.svg)](https://opencollective.com/verdaccio/sponsor/3/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/4/avatar.svg)](https://opencollective.com/verdaccio/sponsor/4/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/5/avatar.svg)](https://opencollective.com/verdaccio/sponsor/5/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/6/avatar.svg)](https://opencollective.com/verdaccio/sponsor/6/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/7/avatar.svg)](https://opencollective.com/verdaccio/sponsor/7/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/8/avatar.svg)](https://opencollective.com/verdaccio/sponsor/8/website)\n[![sponsor](https://opencollective.com/verdaccio/sponsor/9/avatar.svg)](https://opencollective.com/verdaccio/sponsor/9/website)\n\n## Open Collective Backers\n\nThank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/verdaccio#backer)]\n\n[![backers](https://opencollective.com/verdaccio/backers.svg?width=890)](https://opencollective.com/verdaccio#backers)\n\n## Special Thanks\n\nThanks to the following companies to help us to achieve our goals providing free open source licenses.\n\n[![jetbrain](assets/thanks/jetbrains/logo.png)](https://www.jetbrains.com/)\n[![crowdin](assets/thanks/crowdin/logo.png)](https://crowdin.com/)\n[![balsamiq](assets/thanks/balsamiq/logo.jpg)](https://balsamiq.com/)\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].\n\n[![contributors](https://opencollective.com/verdaccio/contributors.svg?width=890&button=true)](../../graphs/contributors)\n\n### FAQ / Contact / Troubleshoot\n\nIf you have any issue you can try the following options, do no desist to ask or check our issues database, perhaps someone has asked already what you are looking for.\n\n- [Blog](https://verdaccio.org/blog/)\n- [Donations](https://opencollective.com/verdaccio)\n- [Reporting an issue](https://github.com/verdaccio/verdaccio/blob/master/CONTRIBUTING.md#reporting-a-bug)\n- [Running discussions](https://github.com/verdaccio/verdaccio/issues?q=is%3Aissue+is%3Aopen+label%3Adiscuss)\n- [Chat](http://chat.verdaccio.org/)\n- [Logos](https://verdaccio.org/docs/en/logo)\n- [Docker Examples](https://github.com/verdaccio/docker-examples)\n- [FAQ](https://github.com/verdaccio/verdaccio/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3Aquestion%20)\n\n### License\n\nVerdaccio is [MIT licensed](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)\n\nThe Verdaccio documentation and logos (excluding /thanks, e.g., .md, .png, .sketch) files within the /assets folder) is\n[Creative Commons licensed](https://github.com/verdaccio/verdaccio/blob/master/LICENSE-docs).\n"
package/src/index.ts CHANGED
@@ -1 +1,17 @@
1
- export * from './middleware';
1
+ export { match } from './middlewares/match';
2
+ export { setSecurityWebHeaders } from './middlewares/security-headers';
3
+ export { validateName, validatePackage } from './middlewares/validation';
4
+ export { media } from './middlewares/media';
5
+ export { encodeScopePackage } from './middlewares/encode-pkg';
6
+ export { expectJson } from './middlewares/json';
7
+ export { antiLoop } from './middlewares/antiLoop';
8
+ export { final } from './middlewares/final';
9
+ export { allow } from './middlewares/allow';
10
+ export { errorReportingMiddleware, handleError } from './middlewares/error';
11
+ export {
12
+ log,
13
+ LOG_STATUS_MESSAGE,
14
+ LOG_VERDACCIO_BYTES,
15
+ LOG_VERDACCIO_ERROR,
16
+ } from './middlewares/log';
17
+ export * from './types';
@@ -0,0 +1,40 @@
1
+ import { API_ERROR, errorUtils } from '@verdaccio/core';
2
+ import { logger } from '@verdaccio/logger';
3
+ import { getVersionFromTarball } from '@verdaccio/utils';
4
+
5
+ import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
6
+
7
+ export function allow<T>(auth: T): Function {
8
+ return function (action: string): Function {
9
+ return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
10
+ req.pause();
11
+ const packageName = req.params.scope
12
+ ? `@${req.params.scope}/${req.params.package}`
13
+ : req.params.package;
14
+ const packageVersion = req.params.filename
15
+ ? getVersionFromTarball(req.params.filename)
16
+ : undefined;
17
+ const remote = req.remote_user;
18
+ logger.trace(
19
+ { action, user: remote?.name },
20
+ `[middleware/allow][@{action}] allow for @{user}`
21
+ );
22
+ auth['allow_' + action](
23
+ { packageName, packageVersion },
24
+ remote,
25
+ function (error, allowed): void {
26
+ req.resume();
27
+ if (error) {
28
+ next(error);
29
+ } else if (allowed) {
30
+ next();
31
+ } else {
32
+ // last plugin (that's our built-in one) returns either
33
+ // cb(err) or cb(null, true), so this should never happen
34
+ throw errorUtils.getInternalError(API_ERROR.PLUGIN_ERROR);
35
+ }
36
+ }
37
+ );
38
+ };
39
+ };
40
+ }
@@ -0,0 +1,30 @@
1
+ import { HTTP_STATUS, errorUtils } from '@verdaccio/core';
2
+ import { Config } from '@verdaccio/types';
3
+
4
+ import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
5
+
6
+ /**
7
+ * A middleware that avoid a registry points itself as proxy and avoid create infinite loops.
8
+ * @param config
9
+ * @returns
10
+ */
11
+ export function antiLoop(config: Config): Function {
12
+ return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
13
+ if (req?.headers?.via != null) {
14
+ const arr = req.get('via')?.split(',');
15
+ if (Array.isArray(arr)) {
16
+ for (let i = 0; i < arr.length; i++) {
17
+ // the "via" header must contains an specific headers, this has to be on sync
18
+ // with the proxy request
19
+ // match eg: Server 1 or Server 2
20
+ // TODO: improve this RegEX
21
+ const m = arr[i].trim().match(/\s*(\S+)\s+(\S+)/);
22
+ if (m && m[2] === config.server_id) {
23
+ return next(errorUtils.getCode(HTTP_STATUS.LOOP_DETECTED, 'loop detected'));
24
+ }
25
+ }
26
+ }
27
+ }
28
+ next();
29
+ };
30
+ }
@@ -0,0 +1,19 @@
1
+ import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
2
+
3
+ /**
4
+ * Encode / in a scoped package name to be matched as a single parameter in routes
5
+ * @param req
6
+ * @param res
7
+ * @param next
8
+ */
9
+ export function encodeScopePackage(
10
+ req: $RequestExtend,
11
+ res: $ResponseExtend,
12
+ next: $NextFunctionVer
13
+ ): void {
14
+ if (req.url.indexOf('@') !== -1) {
15
+ // e.g.: /@org/pkg/1.2.3 -> /@org%2Fpkg/1.2.3, /@org%2Fpkg/1.2.3 -> /@org%2Fpkg/1.2.3
16
+ req.url = req.url.replace(/^(\/@[^\/%]+)\/(?!$)/, '$1%2F');
17
+ }
18
+ next();
19
+ }
@@ -0,0 +1,78 @@
1
+ import buildDebug from 'debug';
2
+ import { HttpError } from 'http-errors';
3
+ import _ from 'lodash';
4
+
5
+ import { API_ERROR, HTTP_STATUS, VerdaccioError } from '@verdaccio/core';
6
+ import { logger } from '@verdaccio/logger';
7
+
8
+ import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
9
+
10
+ const debug = buildDebug('verdaccio:middleware:error');
11
+
12
+ export function handleError(
13
+ err: HttpError,
14
+ req: $RequestExtend,
15
+ res: $ResponseExtend,
16
+ next: $NextFunctionVer
17
+ ) {
18
+ debug('error handler init');
19
+ if (_.isError(err)) {
20
+ debug('is native error');
21
+ if (err.code === 'ECONNABORT' && res.statusCode === HTTP_STATUS.NOT_MODIFIED) {
22
+ return next();
23
+ }
24
+ if (_.isFunction(res.locals.report_error) === false) {
25
+ debug('is locals error report ref');
26
+ // in case of very early error this middleware may not be loaded before error is generated
27
+ // fixing that
28
+ errorReportingMiddleware(req, res, _.noop);
29
+ }
30
+ debug('set locals error report ref');
31
+ res.locals.report_error(err);
32
+ } else {
33
+ // Fall to Middleware.final
34
+ debug('no error to report, jump next layer');
35
+ return next(err);
36
+ }
37
+ }
38
+
39
+ // Middleware
40
+ export function errorReportingMiddleware(
41
+ req: $RequestExtend,
42
+ res: $ResponseExtend,
43
+ next: $NextFunctionVer
44
+ ): void {
45
+ debug('error report middleware');
46
+ res.locals.report_error =
47
+ res.locals.report_error ||
48
+ function (err: VerdaccioError): void {
49
+ if (err.status && err.status >= HTTP_STATUS.BAD_REQUEST && err.status < 600) {
50
+ debug('is error > 409 %o', err?.status);
51
+ if (_.isNil(res.headersSent) === false) {
52
+ debug('send status %o', err?.status);
53
+ res.status(err.status);
54
+ debug('next layer %o', err?.message);
55
+ next({ error: err.message || API_ERROR.UNKNOWN_ERROR });
56
+ }
57
+ } else {
58
+ debug('is error < 409 %o', err?.status);
59
+ logger.error({ err: err }, 'unexpected error: @{!err.message}\n@{err.stack}');
60
+ if (!res.status || !res.send) {
61
+ // TODO: decide which debug keep
62
+ logger.error('this is an error in express.js, please report this');
63
+ debug('this is an error in express.js, please report this, destroy response %o', err);
64
+ res.destroy();
65
+ } else if (!res.headersSent) {
66
+ debug('report internal error %o', err);
67
+ res.status(HTTP_STATUS.INTERNAL_ERROR);
68
+ next({ error: API_ERROR.INTERNAL_SERVER_ERROR });
69
+ } else {
70
+ // socket should be already closed
71
+ debug('this should not happen, otherwise report %o', err);
72
+ }
73
+ }
74
+ };
75
+
76
+ debug('error report middleware next()');
77
+ next();
78
+ }
@@ -0,0 +1,60 @@
1
+ import _ from 'lodash';
2
+
3
+ import { HEADERS, HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER } from '@verdaccio/core';
4
+ import { Manifest } from '@verdaccio/types';
5
+ import { stringToMD5 } from '@verdaccio/utils';
6
+
7
+ import { $NextFunctionVer, $RequestExtend, $ResponseExtend, MiddlewareError } from '../types';
8
+
9
+ export type FinalBody = Manifest | MiddlewareError | string;
10
+
11
+ export function final(
12
+ body: FinalBody,
13
+ req: $RequestExtend,
14
+ res: $ResponseExtend,
15
+ // if we remove `next` breaks test
16
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
17
+ next: $NextFunctionVer
18
+ ): void {
19
+ if (res.statusCode === HTTP_STATUS.UNAUTHORIZED && !res.getHeader(HEADERS.WWW_AUTH)) {
20
+ res.header(HEADERS.WWW_AUTH, `${TOKEN_BASIC}, ${TOKEN_BEARER}`);
21
+ }
22
+
23
+ try {
24
+ if (_.isString(body) || _.isObject(body)) {
25
+ if (!res.get(HEADERS.CONTENT_TYPE)) {
26
+ res.header(HEADERS.CONTENT_TYPE, HEADERS.JSON);
27
+ }
28
+
29
+ if (typeof body === 'object' && _.isNil(body) === false) {
30
+ if (typeof (body as MiddlewareError).error === 'string') {
31
+ res.locals._verdaccio_error = (body as MiddlewareError).error;
32
+ }
33
+ body = JSON.stringify(body, undefined, ' ') + '\n';
34
+ }
35
+
36
+ // don't send etags with errors
37
+ if (
38
+ !res.statusCode ||
39
+ (res.statusCode >= HTTP_STATUS.OK && res.statusCode < HTTP_STATUS.MULTIPLE_CHOICES)
40
+ ) {
41
+ res.header(HEADERS.ETAG, '"' + stringToMD5(body as string) + '"');
42
+ }
43
+ } else {
44
+ // send(null), send(204), etc.
45
+ }
46
+ } catch (err: any) {
47
+ // if verdaccio sends headers first, and then calls res.send()
48
+ // as an error handler, we can't report error properly,
49
+ // and should just close socket
50
+ if (err.message.match(/set headers after they are sent/)) {
51
+ if (_.isNil(res.socket) === false) {
52
+ res.socket?.destroy();
53
+ }
54
+ return;
55
+ }
56
+ throw err;
57
+ }
58
+
59
+ res.send(body);
60
+ }
@@ -0,0 +1,15 @@
1
+ import { errorUtils } from '@verdaccio/core';
2
+ import { isObject } from '@verdaccio/utils';
3
+
4
+ import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
5
+
6
+ export function expectJson(
7
+ req: $RequestExtend,
8
+ res: $ResponseExtend,
9
+ next: $NextFunctionVer
10
+ ): void {
11
+ if (!isObject(req.body)) {
12
+ return next(errorUtils.getBadRequest("can't parse incoming json"));
13
+ }
14
+ next();
15
+ }
@@ -0,0 +1,103 @@
1
+ import _ from 'lodash';
2
+
3
+ import { logger } from '@verdaccio/logger';
4
+
5
+ import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
6
+
7
+ // FIXME: deprecated, moved to @verdaccio/dev-commons
8
+ export const LOG_STATUS_MESSAGE =
9
+ "@{status}, user: @{user}(@{remoteIP}), req: '@{request.method} @{request.url}'";
10
+ export const LOG_VERDACCIO_ERROR = `${LOG_STATUS_MESSAGE}, error: @{!error}`;
11
+ export const LOG_VERDACCIO_BYTES = `${LOG_STATUS_MESSAGE}, bytes: @{bytes.in}/@{bytes.out}`;
12
+
13
+ export function log(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
14
+ // logger
15
+ req.log = logger.child({ sub: 'in' });
16
+
17
+ const _auth = req.headers.authorization;
18
+ if (_.isNil(_auth) === false) {
19
+ req.headers.authorization = '<Classified>';
20
+ }
21
+
22
+ const _cookie = req.get('cookie');
23
+ if (_.isNil(_cookie) === false) {
24
+ req.headers.cookie = '<Classified>';
25
+ }
26
+
27
+ req.url = req.originalUrl;
28
+ req.log.info({ req: req, ip: req.ip }, "@{ip} requested '@{req.method} @{req.url}'");
29
+ req.originalUrl = req.url;
30
+
31
+ if (_.isNil(_auth) === false) {
32
+ req.headers.authorization = _auth;
33
+ }
34
+
35
+ if (_.isNil(_cookie) === false) {
36
+ req.headers.cookie = _cookie;
37
+ }
38
+
39
+ let bytesin = 0;
40
+ req.on('data', function (chunk): void {
41
+ bytesin += chunk.length;
42
+ });
43
+
44
+ let bytesout = 0;
45
+ const _write = res.write;
46
+ // FIXME: res.write should return boolean
47
+ // @ts-ignore
48
+ res.write = function (buf): boolean {
49
+ bytesout += buf.length;
50
+ /* eslint prefer-rest-params: "off" */
51
+ // @ts-ignore
52
+ _write.apply(res, arguments);
53
+ };
54
+
55
+ const log = function (): void {
56
+ const forwardedFor = req.get('x-forwarded-for');
57
+ const remoteAddress = req.connection.remoteAddress;
58
+ const remoteIP = forwardedFor ? `${forwardedFor} via ${remoteAddress}` : remoteAddress;
59
+ let message;
60
+ if (res.locals._verdaccio_error) {
61
+ message = LOG_VERDACCIO_ERROR;
62
+ } else {
63
+ message = LOG_VERDACCIO_BYTES;
64
+ }
65
+
66
+ req.url = req.originalUrl;
67
+ req.log.http(
68
+ {
69
+ request: {
70
+ method: req.method,
71
+ url: req.url,
72
+ },
73
+ user: req.remote_user?.name || null,
74
+ remoteIP,
75
+ status: res.statusCode,
76
+ error: res.locals._verdaccio_error,
77
+ bytes: {
78
+ in: bytesin,
79
+ out: bytesout,
80
+ },
81
+ },
82
+ message
83
+ );
84
+ req.originalUrl = req.url;
85
+ };
86
+
87
+ req.on('close', function (): void {
88
+ log();
89
+ });
90
+
91
+ const _end = res.end;
92
+ // @ts-ignore
93
+ res.end = function (buf): void {
94
+ if (buf) {
95
+ bytesout += buf.length;
96
+ }
97
+ /* eslint prefer-rest-params: "off" */
98
+ // @ts-ignore
99
+ _end.apply(res, arguments);
100
+ log();
101
+ };
102
+ next();
103
+ }
@@ -0,0 +1,16 @@
1
+ import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
2
+
3
+ export function match(regexp: RegExp): any {
4
+ return function (
5
+ req: $RequestExtend,
6
+ res: $ResponseExtend,
7
+ next: $NextFunctionVer,
8
+ value: string
9
+ ): void {
10
+ if (regexp.exec(value)) {
11
+ next();
12
+ } else {
13
+ next('route');
14
+ }
15
+ };
16
+ }
@@ -0,0 +1,18 @@
1
+ import { HEADER_TYPE, HTTP_STATUS, errorUtils } from '@verdaccio/core';
2
+
3
+ import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
4
+
5
+ export function media(expect: string | null): any {
6
+ return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
7
+ if (req.headers[HEADER_TYPE.CONTENT_TYPE] !== expect) {
8
+ next(
9
+ errorUtils.getCode(
10
+ HTTP_STATUS.UNSUPPORTED_MEDIA,
11
+ 'wrong content-type, expect: ' + expect + ', got: ' + req.get[HEADER_TYPE.CONTENT_TYPE]
12
+ )
13
+ );
14
+ } else {
15
+ next();
16
+ }
17
+ };
18
+ }