@verdaccio/middleware 8.0.0-next-8.3 → 8.0.0-next-8.4
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.
- package/CHANGELOG.md +15 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +8 -0
- package/build/index.js.map +1 -1
- package/build/middlewares/encode-pkg.js +14 -7
- package/build/middlewares/encode-pkg.js.map +1 -1
- package/build/middlewares/log.js +2 -1
- package/build/middlewares/log.js.map +1 -1
- package/build/middlewares/make-url-relative.d.ts +8 -0
- package/build/middlewares/make-url-relative.js +45 -0
- package/build/middlewares/make-url-relative.js.map +1 -0
- package/build/middlewares/user-agent.js +2 -1
- package/build/middlewares/user-agent.js.map +1 -1
- package/package.json +7 -8
- package/src/index.ts +1 -0
- package/src/middlewares/encode-pkg.ts +16 -7
- package/src/middlewares/log.ts +3 -1
- package/src/middlewares/make-url-relative.ts +46 -0
- package/src/middlewares/user-agent.ts +2 -1
- package/test/encode.spec.ts +79 -52
- package/test/make-url-relative.spec.ts +42 -0
- package/test/static/runtime.js +3 -0
- package/test/static/vendors.js +3 -0
- package/test/static/vendor.js +0 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @verdaccio/middleware
|
|
2
2
|
|
|
3
|
+
## 8.0.0-next-8.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 48aa89f: chore: request header constants
|
|
8
|
+
- 58e0d95: fix(middleware): error 404 when getting scoped tarballs
|
|
9
|
+
- 1076a89: chore(middleware): fix syntax of test files
|
|
10
|
+
- e93d6a3: chore: auth package requires logger as parameter
|
|
11
|
+
- Updated dependencies [48aa89f]
|
|
12
|
+
- Updated dependencies [58e0d95]
|
|
13
|
+
- @verdaccio/core@8.0.0-next-8.4
|
|
14
|
+
- @verdaccio/config@8.0.0-next-8.4
|
|
15
|
+
- @verdaccio/url@13.0.0-next-8.4
|
|
16
|
+
- @verdaccio/utils@8.1.0-next-8.4
|
|
17
|
+
|
|
3
18
|
## 8.0.0-next-8.3
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/build/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { match } from './middlewares/match';
|
|
|
2
2
|
export { setSecurityWebHeaders } from './middlewares/security-headers';
|
|
3
3
|
export { validateName, validatePackage } from './middlewares/validation';
|
|
4
4
|
export { media } from './middlewares/media';
|
|
5
|
+
export { makeURLrelative } from './middlewares/make-url-relative';
|
|
5
6
|
export { encodeScopePackage } from './middlewares/encode-pkg';
|
|
6
7
|
export { expectJson } from './middlewares/json';
|
|
7
8
|
export { antiLoop } from './middlewares/antiLoop';
|
package/build/index.js
CHANGED
|
@@ -9,6 +9,7 @@ var _exportNames = {
|
|
|
9
9
|
validateName: true,
|
|
10
10
|
validatePackage: true,
|
|
11
11
|
media: true,
|
|
12
|
+
makeURLrelative: true,
|
|
12
13
|
encodeScopePackage: true,
|
|
13
14
|
expectJson: true,
|
|
14
15
|
antiLoop: true,
|
|
@@ -91,6 +92,12 @@ Object.defineProperty(exports, "log", {
|
|
|
91
92
|
return _log.log;
|
|
92
93
|
}
|
|
93
94
|
});
|
|
95
|
+
Object.defineProperty(exports, "makeURLrelative", {
|
|
96
|
+
enumerable: true,
|
|
97
|
+
get: function () {
|
|
98
|
+
return _makeUrlRelative.makeURLrelative;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
94
101
|
Object.defineProperty(exports, "match", {
|
|
95
102
|
enumerable: true,
|
|
96
103
|
get: function () {
|
|
@@ -149,6 +156,7 @@ var _match = require("./middlewares/match");
|
|
|
149
156
|
var _securityHeaders = require("./middlewares/security-headers");
|
|
150
157
|
var _validation = require("./middlewares/validation");
|
|
151
158
|
var _media = require("./middlewares/media");
|
|
159
|
+
var _makeUrlRelative = require("./middlewares/make-url-relative");
|
|
152
160
|
var _encodePkg = require("./middlewares/encode-pkg");
|
|
153
161
|
var _json = require("./middlewares/json");
|
|
154
162
|
var _antiLoop = require("./middlewares/antiLoop");
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["_match","require","_securityHeaders","_validation","_media","_encodePkg","_json","_antiLoop","_final","_allow","_rateLimit","_userAgent","_web","_error","_log","_types","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get"],"sources":["../src/index.ts"],"sourcesContent":["export { match } from './middlewares/match';\nexport { setSecurityWebHeaders } from './middlewares/security-headers';\nexport { validateName, validatePackage } from './middlewares/validation';\nexport { media } from './middlewares/media';\nexport { encodeScopePackage } from './middlewares/encode-pkg';\nexport { expectJson } from './middlewares/json';\nexport { antiLoop } from './middlewares/antiLoop';\nexport { final } from './middlewares/final';\nexport { allow } from './middlewares/allow';\nexport { rateLimit } from './middlewares/rate-limit';\nexport { userAgent } from './middlewares/user-agent';\nexport { webMiddleware, renderWebMiddleware } from './middlewares/web';\nexport { errorReportingMiddleware, handleError } from './middlewares/error';\nexport {\n log,\n LOG_STATUS_MESSAGE,\n LOG_VERDACCIO_BYTES,\n LOG_VERDACCIO_ERROR,\n} from './middlewares/log';\nexport * from './types';\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":["_match","require","_securityHeaders","_validation","_media","_makeUrlRelative","_encodePkg","_json","_antiLoop","_final","_allow","_rateLimit","_userAgent","_web","_error","_log","_types","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get"],"sources":["../src/index.ts"],"sourcesContent":["export { match } from './middlewares/match';\nexport { setSecurityWebHeaders } from './middlewares/security-headers';\nexport { validateName, validatePackage } from './middlewares/validation';\nexport { media } from './middlewares/media';\nexport { makeURLrelative } from './middlewares/make-url-relative';\nexport { encodeScopePackage } from './middlewares/encode-pkg';\nexport { expectJson } from './middlewares/json';\nexport { antiLoop } from './middlewares/antiLoop';\nexport { final } from './middlewares/final';\nexport { allow } from './middlewares/allow';\nexport { rateLimit } from './middlewares/rate-limit';\nexport { userAgent } from './middlewares/user-agent';\nexport { webMiddleware, renderWebMiddleware } from './middlewares/web';\nexport { errorReportingMiddleware, handleError } from './middlewares/error';\nexport {\n log,\n LOG_STATUS_MESSAGE,\n LOG_VERDACCIO_BYTES,\n LOG_VERDACCIO_ERROR,\n} from './middlewares/log';\nexport * from './types';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,gBAAA,GAAAD,OAAA;AACA,IAAAE,WAAA,GAAAF,OAAA;AACA,IAAAG,MAAA,GAAAH,OAAA;AACA,IAAAI,gBAAA,GAAAJ,OAAA;AACA,IAAAK,UAAA,GAAAL,OAAA;AACA,IAAAM,KAAA,GAAAN,OAAA;AACA,IAAAO,SAAA,GAAAP,OAAA;AACA,IAAAQ,MAAA,GAAAR,OAAA;AACA,IAAAS,MAAA,GAAAT,OAAA;AACA,IAAAU,UAAA,GAAAV,OAAA;AACA,IAAAW,UAAA,GAAAX,OAAA;AACA,IAAAY,IAAA,GAAAZ,OAAA;AACA,IAAAa,MAAA,GAAAb,OAAA;AACA,IAAAc,IAAA,GAAAd,OAAA;AAMA,IAAAe,MAAA,GAAAf,OAAA;AAAAgB,MAAA,CAAAC,IAAA,CAAAF,MAAA,EAAAG,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAJ,MAAA,CAAAI,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAZ,MAAA,CAAAI,GAAA;IAAA;EAAA;AAAA","ignoreList":[]}
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.encodeScopePackage = encodeScopePackage;
|
|
7
7
|
var _debug = _interopRequireDefault(require("debug"));
|
|
8
|
+
var _core = require("@verdaccio/core");
|
|
8
9
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
10
|
const debug = (0, _debug.default)('verdaccio:middleware:encode');
|
|
10
11
|
|
|
@@ -16,16 +17,22 @@ const debug = (0, _debug.default)('verdaccio:middleware:encode');
|
|
|
16
17
|
*/
|
|
17
18
|
function encodeScopePackage(req, res, next) {
|
|
18
19
|
const original = req.url;
|
|
20
|
+
|
|
21
|
+
// Expect relative URLs i.e. should call makeURLrelative before this middleware
|
|
22
|
+
if (!req.url.startsWith('/')) {
|
|
23
|
+
return next(_core.errorUtils.getBadRequest(`Invalid URL: ${req.url} (must be relative)`));
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
// If the @ sign is encoded, we need to decode it first
|
|
20
27
|
// e.g.: /%40org/pkg/1.2.3 -> /@org/pkg/1.2.3
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (req.url
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
// For scoped packages, encode the slash to make it a single path segment/parameter
|
|
29
|
+
// e.g.: /@org/pkg/1.2.3 -> /@org%2Fpkg/1.2.3, /@org%2Fpkg/1.2.3 -> /@org%2Fpkg/1.2.3
|
|
30
|
+
req.url = req.url.replace(/^\/%40/, '/@').replace(/^(\/@[^\/%]+)\/(?!$)/, '$1%2F');
|
|
31
|
+
if (original !== req.url) {
|
|
32
|
+
debug('encodeScopePackage: %o -> %o', original, req.url);
|
|
33
|
+
} else {
|
|
34
|
+
debug('encodeScopePackage: %o (unchanged)', original);
|
|
27
35
|
}
|
|
28
|
-
debug('encodeScopePackage: %o -> %o', original, req.url);
|
|
29
36
|
next();
|
|
30
37
|
}
|
|
31
38
|
//# sourceMappingURL=encode-pkg.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encode-pkg.js","names":["_debug","_interopRequireDefault","require","e","__esModule","default","debug","buildDebug","encodeScopePackage","req","res","next","original","url","
|
|
1
|
+
{"version":3,"file":"encode-pkg.js","names":["_debug","_interopRequireDefault","require","_core","e","__esModule","default","debug","buildDebug","encodeScopePackage","req","res","next","original","url","startsWith","errorUtils","getBadRequest","replace"],"sources":["../../src/middlewares/encode-pkg.ts"],"sourcesContent":["import buildDebug from 'debug';\n\nimport { errorUtils } from '@verdaccio/core';\n\nimport { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';\n\nconst debug = buildDebug('verdaccio:middleware:encode');\n\n/**\n * Encode / in a scoped package name to be matched as a single parameter in routes\n * @param req\n * @param res\n * @param next\n */\nexport function encodeScopePackage(\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n): void {\n const original = req.url;\n\n // Expect relative URLs i.e. should call makeURLrelative before this middleware\n if (!req.url.startsWith('/')) {\n return next(errorUtils.getBadRequest(`Invalid URL: ${req.url} (must be relative)`));\n }\n\n // If the @ sign is encoded, we need to decode it first\n // e.g.: /%40org/pkg/1.2.3 -> /@org/pkg/1.2.3\n // For scoped packages, encode the slash to make it a single path segment/parameter\n // e.g.: /@org/pkg/1.2.3 -> /@org%2Fpkg/1.2.3, /@org%2Fpkg/1.2.3 -> /@org%2Fpkg/1.2.3\n req.url = req.url.replace(/^\\/%40/, '/@').replace(/^(\\/@[^\\/%]+)\\/(?!$)/, '$1%2F');\n\n if (original !== req.url) {\n debug('encodeScopePackage: %o -> %o', original, req.url);\n } else {\n debug('encodeScopePackage: %o (unchanged)', original);\n }\n next();\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AAEA,IAAAC,KAAA,GAAAD,OAAA;AAA6C,SAAAD,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAI7C,MAAMG,KAAK,GAAG,IAAAC,cAAU,EAAC,6BAA6B,CAAC;;AAEvD;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,kBAAkBA,CAChCC,GAAmB,EACnBC,GAAoB,EACpBC,IAAsB,EAChB;EACN,MAAMC,QAAQ,GAAGH,GAAG,CAACI,GAAG;;EAExB;EACA,IAAI,CAACJ,GAAG,CAACI,GAAG,CAACC,UAAU,CAAC,GAAG,CAAC,EAAE;IAC5B,OAAOH,IAAI,CAACI,gBAAU,CAACC,aAAa,CAAC,gBAAgBP,GAAG,CAACI,GAAG,qBAAqB,CAAC,CAAC;EACrF;;EAEA;EACA;EACA;EACA;EACAJ,GAAG,CAACI,GAAG,GAAGJ,GAAG,CAACI,GAAG,CAACI,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAACA,OAAO,CAAC,sBAAsB,EAAE,OAAO,CAAC;EAElF,IAAIL,QAAQ,KAAKH,GAAG,CAACI,GAAG,EAAE;IACxBP,KAAK,CAAC,8BAA8B,EAAEM,QAAQ,EAAEH,GAAG,CAACI,GAAG,CAAC;EAC1D,CAAC,MAAM;IACLP,KAAK,CAAC,oCAAoC,EAAEM,QAAQ,CAAC;EACvD;EACAD,IAAI,CAAC,CAAC;AACR","ignoreList":[]}
|
package/build/middlewares/log.js
CHANGED
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.log = exports.LOG_VERDACCIO_ERROR = exports.LOG_VERDACCIO_BYTES = exports.LOG_STATUS_MESSAGE = void 0;
|
|
7
7
|
var _lodash = _interopRequireDefault(require("lodash"));
|
|
8
|
+
var _core = require("@verdaccio/core");
|
|
8
9
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
10
|
// FIXME: deprecated, moved to @verdaccio/dev-commons
|
|
10
11
|
const LOG_STATUS_MESSAGE = exports.LOG_STATUS_MESSAGE = "@{status}, user: @{user}(@{remoteIP}), req: '@{request.method} @{request.url}'";
|
|
@@ -51,7 +52,7 @@ const log = logger => {
|
|
|
51
52
|
_write.apply(res, arguments);
|
|
52
53
|
};
|
|
53
54
|
const log = function () {
|
|
54
|
-
const forwardedFor = req.get(
|
|
55
|
+
const forwardedFor = req.get(_core.HEADERS.FORWARDED_FOR);
|
|
55
56
|
const remoteAddress = req.connection.remoteAddress;
|
|
56
57
|
const remoteIP = forwardedFor ? `${forwardedFor} via ${remoteAddress}` : remoteAddress;
|
|
57
58
|
let message;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.js","names":["_lodash","_interopRequireDefault","require","e","__esModule","default","LOG_STATUS_MESSAGE","exports","LOG_VERDACCIO_ERROR","LOG_VERDACCIO_BYTES","log","logger","req","res","next","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 { $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 const log = (logger) => {\n return 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(
|
|
1
|
+
{"version":3,"file":"log.js","names":["_lodash","_interopRequireDefault","require","_core","e","__esModule","default","LOG_STATUS_MESSAGE","exports","LOG_VERDACCIO_ERROR","LOG_VERDACCIO_BYTES","log","logger","req","res","next","child","sub","_auth","headers","authorization","_","isNil","_cookie","get","cookie","url","originalUrl","info","ip","bytesin","on","chunk","length","bytesout","_write","write","buf","apply","arguments","forwardedFor","HEADERS","FORWARDED_FOR","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 { HEADERS } from '@verdaccio/core';\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 const log = (logger) => {\n return 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(HEADERS.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};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Removes the host from the URL and turns it into a relative URL.
|
|
4
|
+
* @param req
|
|
5
|
+
* @param res
|
|
6
|
+
* @param next
|
|
7
|
+
*/
|
|
8
|
+
export declare function makeURLrelative(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.makeURLrelative = makeURLrelative;
|
|
7
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
8
|
+
var _nodeUrl = require("node:url");
|
|
9
|
+
var _core = require("@verdaccio/core");
|
|
10
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
const debug = (0, _debug.default)('verdaccio:middleware:make-url-relative');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Removes the host from the URL and turns it into a relative URL.
|
|
15
|
+
* @param req
|
|
16
|
+
* @param res
|
|
17
|
+
* @param next
|
|
18
|
+
*/
|
|
19
|
+
function makeURLrelative(req, res, next) {
|
|
20
|
+
const original = req.url;
|
|
21
|
+
|
|
22
|
+
// npm requests can contain the full URL, including the hostname, for example:
|
|
23
|
+
// tarball downloads. Removing the hostname makes the URL relative and allows
|
|
24
|
+
// the application to handle requests in a more consistent way.
|
|
25
|
+
|
|
26
|
+
let url;
|
|
27
|
+
try {
|
|
28
|
+
// In productive use, the URL is absolute (and base will be ignored)
|
|
29
|
+
// In tests, the URL might brelative (and base will be used)
|
|
30
|
+
// https://nodejs.org/docs/latest/api/url.html#new-urlinput-base
|
|
31
|
+
url = new _nodeUrl.URL(req.url, `${req.protocol}://${req.headers.host}/`);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return next(_core.errorUtils.getBadRequest(`Invalid URL: ${req.url} (${error})`));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Rebuild the URL without hostname
|
|
37
|
+
req.url = url.pathname + url.search + url.hash;
|
|
38
|
+
if (original !== req.url) {
|
|
39
|
+
debug('makeURLrelative: %o -> %o', original, req.url);
|
|
40
|
+
} else {
|
|
41
|
+
debug('makeURLrelative: %o (unchanged)', original);
|
|
42
|
+
}
|
|
43
|
+
next();
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=make-url-relative.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"make-url-relative.js","names":["_debug","_interopRequireDefault","require","_nodeUrl","_core","e","__esModule","default","debug","buildDebug","makeURLrelative","req","res","next","original","url","URL","protocol","headers","host","error","errorUtils","getBadRequest","pathname","search","hash"],"sources":["../../src/middlewares/make-url-relative.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { URL } from 'node:url';\n\nimport { errorUtils } from '@verdaccio/core';\n\nimport { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';\n\nconst debug = buildDebug('verdaccio:middleware:make-url-relative');\n\n/**\n * Removes the host from the URL and turns it into a relative URL.\n * @param req\n * @param res\n * @param next\n */\nexport function makeURLrelative(\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n): void {\n const original = req.url;\n\n // npm requests can contain the full URL, including the hostname, for example:\n // tarball downloads. Removing the hostname makes the URL relative and allows\n // the application to handle requests in a more consistent way.\n\n let url;\n try {\n // In productive use, the URL is absolute (and base will be ignored)\n // In tests, the URL might brelative (and base will be used)\n // https://nodejs.org/docs/latest/api/url.html#new-urlinput-base\n url = new URL(req.url, `${req.protocol}://${req.headers.host}/`);\n } catch (error) {\n return next(errorUtils.getBadRequest(`Invalid URL: ${req.url} (${error})`));\n }\n\n // Rebuild the URL without hostname\n req.url = url.pathname + url.search + url.hash;\n\n if (original !== req.url) {\n debug('makeURLrelative: %o -> %o', original, req.url);\n } else {\n debug('makeURLrelative: %o (unchanged)', original);\n }\n next();\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,QAAA,GAAAD,OAAA;AAEA,IAAAE,KAAA,GAAAF,OAAA;AAA6C,SAAAD,uBAAAI,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAI7C,MAAMG,KAAK,GAAG,IAAAC,cAAU,EAAC,wCAAwC,CAAC;;AAElE;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,eAAeA,CAC7BC,GAAmB,EACnBC,GAAoB,EACpBC,IAAsB,EAChB;EACN,MAAMC,QAAQ,GAAGH,GAAG,CAACI,GAAG;;EAExB;EACA;EACA;;EAEA,IAAIA,GAAG;EACP,IAAI;IACF;IACA;IACA;IACAA,GAAG,GAAG,IAAIC,YAAG,CAACL,GAAG,CAACI,GAAG,EAAE,GAAGJ,GAAG,CAACM,QAAQ,MAAMN,GAAG,CAACO,OAAO,CAACC,IAAI,GAAG,CAAC;EAClE,CAAC,CAAC,OAAOC,KAAK,EAAE;IACd,OAAOP,IAAI,CAACQ,gBAAU,CAACC,aAAa,CAAC,gBAAgBX,GAAG,CAACI,GAAG,KAAKK,KAAK,GAAG,CAAC,CAAC;EAC7E;;EAEA;EACAT,GAAG,CAACI,GAAG,GAAGA,GAAG,CAACQ,QAAQ,GAAGR,GAAG,CAACS,MAAM,GAAGT,GAAG,CAACU,IAAI;EAE9C,IAAIX,QAAQ,KAAKH,GAAG,CAACI,GAAG,EAAE;IACxBP,KAAK,CAAC,2BAA2B,EAAEM,QAAQ,EAAEH,GAAG,CAACI,GAAG,CAAC;EACvD,CAAC,MAAM;IACLP,KAAK,CAAC,iCAAiC,EAAEM,QAAQ,CAAC;EACpD;EACAD,IAAI,CAAC,CAAC;AACR","ignoreList":[]}
|
|
@@ -5,9 +5,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.userAgent = userAgent;
|
|
7
7
|
var _config = require("@verdaccio/config");
|
|
8
|
+
var _core = require("@verdaccio/core");
|
|
8
9
|
function userAgent(config) {
|
|
9
10
|
return function (_req, res, next) {
|
|
10
|
-
res.setHeader(
|
|
11
|
+
res.setHeader(_core.HEADERS.POWERED_BY, (0, _config.getUserAgent)(config?.user_agent));
|
|
11
12
|
next();
|
|
12
13
|
};
|
|
13
14
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-agent.js","names":["_config","require","userAgent","config","_req","res","next","setHeader","getUserAgent","user_agent"],"sources":["../../src/middlewares/user-agent.ts"],"sourcesContent":["import { getUserAgent } from '@verdaccio/config';\n\nimport { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';\n\nexport function userAgent(config) {\n return function (_req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {\n res.setHeader(
|
|
1
|
+
{"version":3,"file":"user-agent.js","names":["_config","require","_core","userAgent","config","_req","res","next","setHeader","HEADERS","POWERED_BY","getUserAgent","user_agent"],"sources":["../../src/middlewares/user-agent.ts"],"sourcesContent":["import { getUserAgent } from '@verdaccio/config';\nimport { HEADERS } from '@verdaccio/core';\n\nimport { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';\n\nexport function userAgent(config) {\n return function (_req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {\n res.setHeader(HEADERS.POWERED_BY, getUserAgent(config?.user_agent));\n next();\n };\n}\n"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,KAAA,GAAAD,OAAA;AAIO,SAASE,SAASA,CAACC,MAAM,EAAE;EAChC,OAAO,UAAUC,IAAoB,EAAEC,GAAoB,EAAEC,IAAsB,EAAQ;IACzFD,GAAG,CAACE,SAAS,CAACC,aAAO,CAACC,UAAU,EAAE,IAAAC,oBAAY,EAACP,MAAM,EAAEQ,UAAU,CAAC,CAAC;IACnEL,IAAI,CAAC,CAAC;EACR,CAAC;AACH","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verdaccio/middleware",
|
|
3
|
-
"version": "8.0.0-next-8.
|
|
3
|
+
"version": "8.0.0-next-8.4",
|
|
4
4
|
"description": "express middleware utils",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
"node": ">=18"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@verdaccio/config": "8.0.0-next-8.
|
|
33
|
-
"@verdaccio/core": "8.0.0-next-8.
|
|
34
|
-
"@verdaccio/url": "13.0.0-next-8.
|
|
35
|
-
"@verdaccio/utils": "8.1.0-next-8.
|
|
32
|
+
"@verdaccio/config": "8.0.0-next-8.4",
|
|
33
|
+
"@verdaccio/core": "8.0.0-next-8.4",
|
|
34
|
+
"@verdaccio/url": "13.0.0-next-8.4",
|
|
35
|
+
"@verdaccio/utils": "8.1.0-next-8.4",
|
|
36
36
|
"debug": "4.3.7",
|
|
37
|
-
"express": "4.21.
|
|
37
|
+
"express": "4.21.1",
|
|
38
38
|
"express-rate-limit": "5.5.1",
|
|
39
39
|
"lodash": "4.17.21",
|
|
40
40
|
"lru-cache": "7.18.3",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"url": "https://opencollective.com/verdaccio"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@verdaccio/logger": "8.0.0-next-8.
|
|
48
|
+
"@verdaccio/logger": "8.0.0-next-8.4",
|
|
49
49
|
"body-parser": "1.20.3",
|
|
50
50
|
"supertest": "7.0.0",
|
|
51
51
|
"jsdom": "25.0.1"
|
|
@@ -57,7 +57,6 @@
|
|
|
57
57
|
"build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
|
|
58
58
|
"watch": "pnpm build:js -- --watch",
|
|
59
59
|
"test": "vitest run",
|
|
60
|
-
"test:snap": "jest --updateSnapshot",
|
|
61
60
|
"build": "pnpm run build:js && pnpm run build:types"
|
|
62
61
|
}
|
|
63
62
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { match } from './middlewares/match';
|
|
|
2
2
|
export { setSecurityWebHeaders } from './middlewares/security-headers';
|
|
3
3
|
export { validateName, validatePackage } from './middlewares/validation';
|
|
4
4
|
export { media } from './middlewares/media';
|
|
5
|
+
export { makeURLrelative } from './middlewares/make-url-relative';
|
|
5
6
|
export { encodeScopePackage } from './middlewares/encode-pkg';
|
|
6
7
|
export { expectJson } from './middlewares/json';
|
|
7
8
|
export { antiLoop } from './middlewares/antiLoop';
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import buildDebug from 'debug';
|
|
2
2
|
|
|
3
|
+
import { errorUtils } from '@verdaccio/core';
|
|
4
|
+
|
|
3
5
|
import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
|
|
4
6
|
|
|
5
7
|
const debug = buildDebug('verdaccio:middleware:encode');
|
|
@@ -16,15 +18,22 @@ export function encodeScopePackage(
|
|
|
16
18
|
next: $NextFunctionVer
|
|
17
19
|
): void {
|
|
18
20
|
const original = req.url;
|
|
21
|
+
|
|
22
|
+
// Expect relative URLs i.e. should call makeURLrelative before this middleware
|
|
23
|
+
if (!req.url.startsWith('/')) {
|
|
24
|
+
return next(errorUtils.getBadRequest(`Invalid URL: ${req.url} (must be relative)`));
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
// If the @ sign is encoded, we need to decode it first
|
|
20
28
|
// e.g.: /%40org/pkg/1.2.3 -> /@org/pkg/1.2.3
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
// For scoped packages, encode the slash to make it a single path segment/parameter
|
|
30
|
+
// e.g.: /@org/pkg/1.2.3 -> /@org%2Fpkg/1.2.3, /@org%2Fpkg/1.2.3 -> /@org%2Fpkg/1.2.3
|
|
31
|
+
req.url = req.url.replace(/^\/%40/, '/@').replace(/^(\/@[^\/%]+)\/(?!$)/, '$1%2F');
|
|
32
|
+
|
|
33
|
+
if (original !== req.url) {
|
|
34
|
+
debug('encodeScopePackage: %o -> %o', original, req.url);
|
|
35
|
+
} else {
|
|
36
|
+
debug('encodeScopePackage: %o (unchanged)', original);
|
|
27
37
|
}
|
|
28
|
-
debug('encodeScopePackage: %o -> %o', original, req.url);
|
|
29
38
|
next();
|
|
30
39
|
}
|
package/src/middlewares/log.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
|
|
3
|
+
import { HEADERS } from '@verdaccio/core';
|
|
4
|
+
|
|
3
5
|
import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
|
|
4
6
|
|
|
5
7
|
// FIXME: deprecated, moved to @verdaccio/dev-commons
|
|
@@ -52,7 +54,7 @@ export const log = (logger) => {
|
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
const log = function (): void {
|
|
55
|
-
const forwardedFor = req.get(
|
|
57
|
+
const forwardedFor = req.get(HEADERS.FORWARDED_FOR);
|
|
56
58
|
const remoteAddress = req.connection.remoteAddress;
|
|
57
59
|
const remoteIP = forwardedFor ? `${forwardedFor} via ${remoteAddress}` : remoteAddress;
|
|
58
60
|
let message;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import buildDebug from 'debug';
|
|
2
|
+
import { URL } from 'node:url';
|
|
3
|
+
|
|
4
|
+
import { errorUtils } from '@verdaccio/core';
|
|
5
|
+
|
|
6
|
+
import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
|
|
7
|
+
|
|
8
|
+
const debug = buildDebug('verdaccio:middleware:make-url-relative');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Removes the host from the URL and turns it into a relative URL.
|
|
12
|
+
* @param req
|
|
13
|
+
* @param res
|
|
14
|
+
* @param next
|
|
15
|
+
*/
|
|
16
|
+
export function makeURLrelative(
|
|
17
|
+
req: $RequestExtend,
|
|
18
|
+
res: $ResponseExtend,
|
|
19
|
+
next: $NextFunctionVer
|
|
20
|
+
): void {
|
|
21
|
+
const original = req.url;
|
|
22
|
+
|
|
23
|
+
// npm requests can contain the full URL, including the hostname, for example:
|
|
24
|
+
// tarball downloads. Removing the hostname makes the URL relative and allows
|
|
25
|
+
// the application to handle requests in a more consistent way.
|
|
26
|
+
|
|
27
|
+
let url;
|
|
28
|
+
try {
|
|
29
|
+
// In productive use, the URL is absolute (and base will be ignored)
|
|
30
|
+
// In tests, the URL might brelative (and base will be used)
|
|
31
|
+
// https://nodejs.org/docs/latest/api/url.html#new-urlinput-base
|
|
32
|
+
url = new URL(req.url, `${req.protocol}://${req.headers.host}/`);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return next(errorUtils.getBadRequest(`Invalid URL: ${req.url} (${error})`));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Rebuild the URL without hostname
|
|
38
|
+
req.url = url.pathname + url.search + url.hash;
|
|
39
|
+
|
|
40
|
+
if (original !== req.url) {
|
|
41
|
+
debug('makeURLrelative: %o -> %o', original, req.url);
|
|
42
|
+
} else {
|
|
43
|
+
debug('makeURLrelative: %o (unchanged)', original);
|
|
44
|
+
}
|
|
45
|
+
next();
|
|
46
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { getUserAgent } from '@verdaccio/config';
|
|
2
|
+
import { HEADERS } from '@verdaccio/core';
|
|
2
3
|
|
|
3
4
|
import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types';
|
|
4
5
|
|
|
5
6
|
export function userAgent(config) {
|
|
6
7
|
return function (_req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
|
7
|
-
res.setHeader(
|
|
8
|
+
res.setHeader(HEADERS.POWERED_BY, getUserAgent(config?.user_agent));
|
|
8
9
|
next();
|
|
9
10
|
};
|
|
10
11
|
}
|
package/test/encode.spec.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import request from 'supertest';
|
|
2
|
-
import { expect, test } from 'vitest';
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
3
|
|
|
4
4
|
import { HTTP_STATUS } from '@verdaccio/core';
|
|
5
5
|
|
|
6
6
|
import { encodeScopePackage } from '../src';
|
|
7
7
|
import { getApp } from './helper';
|
|
8
8
|
|
|
9
|
-
test('encode is json', async () => {
|
|
9
|
+
test('encode is json with relative path', async () => {
|
|
10
10
|
const app = getApp([]);
|
|
11
11
|
// @ts-ignore
|
|
12
12
|
app.use(encodeScopePackage);
|
|
13
|
-
// @ts-ignore
|
|
14
13
|
app.get('/:id', (req, res) => {
|
|
15
14
|
const { id } = req.params;
|
|
16
15
|
res.status(HTTP_STATUS.OK).json({ id });
|
|
@@ -21,84 +20,112 @@ test('encode is json', async () => {
|
|
|
21
20
|
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
22
21
|
});
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
describe('packages requests', () => {
|
|
25
24
|
const app = getApp([]);
|
|
26
25
|
// @ts-ignore
|
|
27
26
|
app.use(encodeScopePackage);
|
|
28
|
-
// @ts-ignore
|
|
29
27
|
app.get('/:package/:version?', (req, res) => {
|
|
30
28
|
const { package: pkg, version } = req.params;
|
|
31
29
|
res.status(HTTP_STATUS.OK).json({ package: pkg, version });
|
|
32
30
|
});
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
test('just package', async () => {
|
|
33
|
+
const res = await request(app).get('/foo');
|
|
34
|
+
expect(res.body).toEqual({ package: 'foo' });
|
|
35
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
36
|
+
});
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
test('package with version', async () => {
|
|
39
|
+
const res = await request(app).get('/foo/1.0.0');
|
|
40
|
+
expect(res.body).toEqual({ package: 'foo', version: '1.0.0' });
|
|
41
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
42
|
+
});
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
test('scoped package', async () => {
|
|
45
|
+
const res = await request(app).get('/@scope/foo');
|
|
46
|
+
expect(res.body).toEqual({ package: '@scope/foo' });
|
|
47
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
48
|
+
});
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
test('scoped package with version', async () => {
|
|
51
|
+
const res = await request(app).get('/@scope/foo/1.0.0');
|
|
52
|
+
expect(res.body).toEqual({ package: '@scope/foo', version: '1.0.0' });
|
|
53
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
54
|
+
});
|
|
49
55
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
test('scoped package with encoded path', async () => {
|
|
57
|
+
const res = await request(app).get('/@scope%2ffoo');
|
|
58
|
+
expect(res.body).toEqual({ package: '@scope/foo' });
|
|
59
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
60
|
+
});
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
test('scoped package and version with encoded path', async () => {
|
|
63
|
+
const res = await request(app).get('/@scope%2ffoo/1.0.0');
|
|
64
|
+
expect(res.body).toEqual({ package: '@scope/foo', version: '1.0.0' });
|
|
65
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
66
|
+
});
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
test('scoped package with encoded @ and path ', async () => {
|
|
69
|
+
const res = await request(app).get('/%40scope%2ffoo');
|
|
70
|
+
expect(res.body).toEqual({ package: '@scope/foo' });
|
|
71
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
72
|
+
});
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
test('scoped package and version with encoded @ and path', async () => {
|
|
75
|
+
const res = await request(app).get('/%40scope%2ffoo/1.0.0');
|
|
76
|
+
expect(res.body).toEqual({ package: '@scope/foo', version: '1.0.0' });
|
|
77
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
78
|
+
});
|
|
65
79
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
80
|
+
test('scoped package with encoded @', async () => {
|
|
81
|
+
const res = await request(app).get('/%40scope/foo');
|
|
82
|
+
expect(res.body).toEqual({ package: '@scope/foo' });
|
|
83
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
84
|
+
});
|
|
69
85
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
86
|
+
test('scoped package and version with encoded @', async () => {
|
|
87
|
+
const res = await request(app).get('/%40scope/foo/1.0.0');
|
|
88
|
+
expect(res.body).toEqual({ package: '@scope/foo', version: '1.0.0' });
|
|
89
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
90
|
+
});
|
|
73
91
|
});
|
|
74
92
|
|
|
75
|
-
|
|
93
|
+
describe('tarball requests', () => {
|
|
76
94
|
const app = getApp([]);
|
|
77
95
|
// @ts-ignore
|
|
78
96
|
app.use(encodeScopePackage);
|
|
79
|
-
// @ts-ignore
|
|
80
97
|
app.get('/:package/-/:filename', (req, res) => {
|
|
81
98
|
const { package: pkg, filename } = req.params;
|
|
82
99
|
res.status(HTTP_STATUS.OK).json({ package: pkg, filename });
|
|
83
100
|
});
|
|
84
101
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
102
|
+
test('just package', async () => {
|
|
103
|
+
const res = await request(app).get('/foo/-/foo-1.2.3.tgz');
|
|
104
|
+
expect(res.body).toEqual({ package: 'foo', filename: 'foo-1.2.3.tgz' });
|
|
105
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
106
|
+
});
|
|
88
107
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
108
|
+
test('scoped package', async () => {
|
|
109
|
+
const res = await request(app).get('/@scope/foo/-/foo-1.2.3.tgz');
|
|
110
|
+
expect(res.body).toEqual({ package: '@scope/foo', filename: 'foo-1.2.3.tgz' });
|
|
111
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
112
|
+
});
|
|
92
113
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
114
|
+
test('scoped package with encoded path', async () => {
|
|
115
|
+
const res = await request(app).get('/@scope%2ffoo/-/foo-1.2.3.tgz');
|
|
116
|
+
expect(res.body).toEqual({ package: '@scope/foo', filename: 'foo-1.2.3.tgz' });
|
|
117
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
118
|
+
});
|
|
96
119
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
120
|
+
test('scoped package with encoded @ and path', async () => {
|
|
121
|
+
const res = await request(app).get('/%40scope%2ffoo/-/foo-1.2.3.tgz');
|
|
122
|
+
expect(res.body).toEqual({ package: '@scope/foo', filename: 'foo-1.2.3.tgz' });
|
|
123
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
124
|
+
});
|
|
100
125
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
126
|
+
test('scoped package with encoded @', async () => {
|
|
127
|
+
const res = await request(app).get('/%40scope/foo/-/foo-1.2.3.tgz');
|
|
128
|
+
expect(res.body).toEqual({ package: '@scope/foo', filename: 'foo-1.2.3.tgz' });
|
|
129
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
130
|
+
});
|
|
104
131
|
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import request from 'supertest';
|
|
2
|
+
import { expect, test } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { HEADERS, HTTP_STATUS } from '@verdaccio/core';
|
|
5
|
+
|
|
6
|
+
import { makeURLrelative } from '../src';
|
|
7
|
+
import { getApp } from './helper';
|
|
8
|
+
|
|
9
|
+
const testHosts = [
|
|
10
|
+
'localhost:4873', // with port
|
|
11
|
+
'myregistry.com', // no port
|
|
12
|
+
'42.42.42.42', // ip
|
|
13
|
+
'[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443', // ip6
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
test.each([testHosts])('remove host from url', async (host) => {
|
|
17
|
+
const app = getApp([]);
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
app.use(makeURLrelative);
|
|
20
|
+
app.get('/:id', (req, res) => {
|
|
21
|
+
const { id } = req.params;
|
|
22
|
+
res.status(HTTP_STATUS.OK).json({ id, url: req.url });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const res = await request(app).get('/foo').set(HEADERS.HOST, host);
|
|
26
|
+
expect(res.body).toEqual({ id: 'foo', url: '/foo' });
|
|
27
|
+
expect(res.status).toEqual(HTTP_STATUS.OK);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('invalid url', async () => {
|
|
31
|
+
const app = getApp([]);
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
app.use(makeURLrelative);
|
|
34
|
+
app.get('/:id', (req, res) => {
|
|
35
|
+
const { id } = req.params;
|
|
36
|
+
res.status(HTTP_STATUS.OK).json({ id });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const res = await request(app).get('/foo').set(HEADERS.HOST, 'invalid::host');
|
|
40
|
+
expect(res.status).toEqual(HTTP_STATUS.BAD_REQUEST);
|
|
41
|
+
expect(res.text).toContain('Invalid URL');
|
|
42
|
+
});
|
package/test/static/vendor.js
DELETED