@verdaccio/middleware 8.0.2 → 8.0.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/build/index.d.ts CHANGED
@@ -7,6 +7,8 @@ export { expectJson } from './middlewares/json';
7
7
  export { antiLoop } from './middlewares/antiLoop';
8
8
  export { final } from './middlewares/final';
9
9
  export { allow } from './middlewares/allow';
10
+ export { enforceGeneratedTokenMetadata } from './middlewares/token-auth';
11
+ export type { TokenReadableStorage } from './middlewares/token-auth';
10
12
  export { rateLimit } from './middlewares/rate-limit';
11
13
  export { registerBodyParser } from './middlewares/body-parser';
12
14
  export { userAgent } from './middlewares/user-agent';
package/build/index.js CHANGED
@@ -14,6 +14,7 @@ var _exportNames = {
14
14
  antiLoop: true,
15
15
  final: true,
16
16
  allow: true,
17
+ enforceGeneratedTokenMetadata: true,
17
18
  rateLimit: true,
18
19
  registerBodyParser: true,
19
20
  userAgent: true,
@@ -64,6 +65,12 @@ Object.defineProperty(exports, "encodeScopePackage", {
64
65
  return _encodePkg.encodeScopePackage;
65
66
  }
66
67
  });
68
+ Object.defineProperty(exports, "enforceGeneratedTokenMetadata", {
69
+ enumerable: true,
70
+ get: function () {
71
+ return _tokenAuth.enforceGeneratedTokenMetadata;
72
+ }
73
+ });
67
74
  Object.defineProperty(exports, "errorReportingMiddleware", {
68
75
  enumerable: true,
69
76
  get: function () {
@@ -175,6 +182,7 @@ var _json = require("./middlewares/json");
175
182
  var _antiLoop = require("./middlewares/antiLoop");
176
183
  var _final = require("./middlewares/final");
177
184
  var _allow = require("./middlewares/allow");
185
+ var _tokenAuth = require("./middlewares/token-auth");
178
186
  var _rateLimit = require("./middlewares/rate-limit");
179
187
  var _bodyParser = require("./middlewares/body-parser");
180
188
  var _userAgent = require("./middlewares/user-agent");
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_match","require","_validation","_media","_makeUrlRelative","_encodePkg","_json","_antiLoop","_final","_allow","_rateLimit","_bodyParser","_userAgent","_web","_error","_requestOptions","_log","_types","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_api_urls","_webUrls"],"sources":["../src/index.ts"],"sourcesContent":["export { match } from './middlewares/match';\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 { registerBodyParser } from './middlewares/body-parser';\nexport { userAgent } from './middlewares/user-agent';\nexport { webMiddleware, renderWebMiddleware, setSecurityWebHeaders } from './middlewares/web';\nexport { errorReportingMiddleware, handleError } from './middlewares/error';\nexport { getRequestOptions } from './middlewares/request-options';\nexport {\n log,\n LOG_STATUS_MESSAGE,\n LOG_VERDACCIO_BYTES,\n LOG_VERDACCIO_ERROR,\n} from './middlewares/log';\nexport * from './types';\nexport * from './middlewares/api_urls';\nexport * from './middlewares/web/web-urls';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,WAAA,GAAAD,OAAA;AACA,IAAAE,MAAA,GAAAF,OAAA;AACA,IAAAG,gBAAA,GAAAH,OAAA;AACA,IAAAI,UAAA,GAAAJ,OAAA;AACA,IAAAK,KAAA,GAAAL,OAAA;AACA,IAAAM,SAAA,GAAAN,OAAA;AACA,IAAAO,MAAA,GAAAP,OAAA;AACA,IAAAQ,MAAA,GAAAR,OAAA;AACA,IAAAS,UAAA,GAAAT,OAAA;AACA,IAAAU,WAAA,GAAAV,OAAA;AACA,IAAAW,UAAA,GAAAX,OAAA;AACA,IAAAY,IAAA,GAAAZ,OAAA;AACA,IAAAa,MAAA,GAAAb,OAAA;AACA,IAAAc,eAAA,GAAAd,OAAA;AACA,IAAAe,IAAA,GAAAf,OAAA;AAMA,IAAAgB,MAAA,GAAAhB,OAAA;AAAAiB,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;AACA,IAAAS,SAAA,GAAA7B,OAAA;AAAAiB,MAAA,CAAAC,IAAA,CAAAW,SAAA,EAAAV,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,MAAAS,SAAA,CAAAT,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,SAAA,CAAAT,GAAA;IAAA;EAAA;AAAA;AACA,IAAAU,QAAA,GAAA9B,OAAA;AAAAiB,MAAA,CAAAC,IAAA,CAAAY,QAAA,EAAAX,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,MAAAU,QAAA,CAAAV,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAE,QAAA,CAAAV,GAAA;IAAA;EAAA;AAAA","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["_match","require","_validation","_media","_makeUrlRelative","_encodePkg","_json","_antiLoop","_final","_allow","_tokenAuth","_rateLimit","_bodyParser","_userAgent","_web","_error","_requestOptions","_log","_types","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_api_urls","_webUrls"],"sources":["../src/index.ts"],"sourcesContent":["export { match } from './middlewares/match';\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 { enforceGeneratedTokenMetadata } from './middlewares/token-auth';\nexport type { TokenReadableStorage } from './middlewares/token-auth';\nexport { rateLimit } from './middlewares/rate-limit';\nexport { registerBodyParser } from './middlewares/body-parser';\nexport { userAgent } from './middlewares/user-agent';\nexport { webMiddleware, renderWebMiddleware, setSecurityWebHeaders } from './middlewares/web';\nexport { errorReportingMiddleware, handleError } from './middlewares/error';\nexport { getRequestOptions } from './middlewares/request-options';\nexport {\n log,\n LOG_STATUS_MESSAGE,\n LOG_VERDACCIO_BYTES,\n LOG_VERDACCIO_ERROR,\n} from './middlewares/log';\nexport * from './types';\nexport * from './middlewares/api_urls';\nexport * from './middlewares/web/web-urls';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,WAAA,GAAAD,OAAA;AACA,IAAAE,MAAA,GAAAF,OAAA;AACA,IAAAG,gBAAA,GAAAH,OAAA;AACA,IAAAI,UAAA,GAAAJ,OAAA;AACA,IAAAK,KAAA,GAAAL,OAAA;AACA,IAAAM,SAAA,GAAAN,OAAA;AACA,IAAAO,MAAA,GAAAP,OAAA;AACA,IAAAQ,MAAA,GAAAR,OAAA;AACA,IAAAS,UAAA,GAAAT,OAAA;AAEA,IAAAU,UAAA,GAAAV,OAAA;AACA,IAAAW,WAAA,GAAAX,OAAA;AACA,IAAAY,UAAA,GAAAZ,OAAA;AACA,IAAAa,IAAA,GAAAb,OAAA;AACA,IAAAc,MAAA,GAAAd,OAAA;AACA,IAAAe,eAAA,GAAAf,OAAA;AACA,IAAAgB,IAAA,GAAAhB,OAAA;AAMA,IAAAiB,MAAA,GAAAjB,OAAA;AAAAkB,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;AACA,IAAAS,SAAA,GAAA9B,OAAA;AAAAkB,MAAA,CAAAC,IAAA,CAAAW,SAAA,EAAAV,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,MAAAS,SAAA,CAAAT,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,SAAA,CAAAT,GAAA;IAAA;EAAA;AAAA;AACA,IAAAU,QAAA,GAAA/B,OAAA;AAAAkB,MAAA,CAAAC,IAAA,CAAAY,QAAA,EAAAX,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,MAAAU,QAAA,CAAAV,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAE,QAAA,CAAAV,GAAA;IAAA;EAAA;AAAA","ignoreList":[]}
@@ -0,0 +1,38 @@
1
+ import type { RequestHandler } from 'express';
2
+ import type { Logger, Token } from '@verdaccio/types';
3
+ /**
4
+ * Minimal storage surface required to enforce generated token metadata. Typed
5
+ * structurally so this package does not need to depend on `@verdaccio/store`.
6
+ */
7
+ export interface TokenReadableStorage {
8
+ /**
9
+ * Load the generated tokens stored for a user.
10
+ *
11
+ * @param filter narrows the lookup to a single `user`
12
+ * @returns the user's persisted tokens
13
+ */
14
+ readTokens(filter: {
15
+ user: string;
16
+ }): Promise<Token[]>;
17
+ }
18
+ /**
19
+ * Express middleware that enforces the metadata of npm-style generated tokens
20
+ * (created via `POST /-/npm/v1/tokens`).
21
+ *
22
+ * A generated token embeds a server-issued `key` that is recovered from the
23
+ * verified credentials as `remote_user.token.key`. This middleware looks that
24
+ * key up in storage and rejects the request when the token:
25
+ * - is missing from storage (revoked or never issued);
26
+ * - is used from a client address outside its `cidr` whitelist;
27
+ * - is `readonly` and the request uses a write method (`DELETE`/`PATCH`/`POST`/`PUT`).
28
+ *
29
+ * Requests that do not carry a generated token key (e.g. interactive logins)
30
+ * pass through untouched. It must run after the JWT/auth middleware has
31
+ * populated `remote_user`. Failures fail closed: a storage lookup error yields
32
+ * an internal error rather than allowing the request.
33
+ *
34
+ * @param storage storage exposing {@link TokenReadableStorage.readTokens} to load the user's tokens
35
+ * @param logger logger used to record rejected attempts and lookup failures
36
+ * @returns an Express {@link RequestHandler} that calls `next()` to allow or `next(error)` to reject
37
+ */
38
+ export declare function enforceGeneratedTokenMetadata(storage: TokenReadableStorage, logger: Logger): RequestHandler;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.enforceGeneratedTokenMetadata = enforceGeneratedTokenMetadata;
7
+ var _core = require("@verdaccio/core");
8
+ const WRITE_METHODS = new Set(['DELETE', 'PATCH', 'POST', 'PUT']);
9
+
10
+ /**
11
+ * Minimal storage surface required to enforce generated token metadata. Typed
12
+ * structurally so this package does not need to depend on `@verdaccio/store`.
13
+ */
14
+
15
+ /**
16
+ * Resolve the client address of an incoming request for CIDR matching.
17
+ *
18
+ * Uses Express' `req.ip`, which already honors the application's `trust proxy`
19
+ * setting (`config.server.trustProxy`): when the operator has declared trusted
20
+ * proxies, `req.ip` is the upstream-most untrusted address parsed from
21
+ * `X-Forwarded-For`; otherwise it is the direct socket peer and the forwarded
22
+ * header is ignored entirely.
23
+ *
24
+ * The middleware must NOT read `X-Forwarded-For` on its own — doing so
25
+ * unconditionally would let any client spoof its address (and bypass a token's
26
+ * CIDR whitelist) simply by sending the header. Falls back to the raw socket
27
+ * address and is normalized (trimmed, IPv4-mapped IPv6 prefix stripped) via
28
+ * {@link ipUtils.normalizeAddress}.
29
+ *
30
+ * @param req the incoming Express request
31
+ * @returns the normalized client address, or `undefined` when none can be determined
32
+ */
33
+ function getClientAddress(req) {
34
+ return _core.ipUtils.normalizeAddress(req.ip || req.socket.remoteAddress);
35
+ }
36
+
37
+ /**
38
+ * Find the stored token whose `key` matches the key carried by the request.
39
+ *
40
+ * @param tokens the tokens persisted for the authenticated user
41
+ * @param tokenKey the key extracted from the presented generated token
42
+ * @returns the matching {@link Token}, or `undefined` when it has been revoked / never existed
43
+ */
44
+ function findToken(tokens, tokenKey) {
45
+ return tokens.find(({
46
+ key
47
+ }) => key === tokenKey);
48
+ }
49
+
50
+ /**
51
+ * Express middleware that enforces the metadata of npm-style generated tokens
52
+ * (created via `POST /-/npm/v1/tokens`).
53
+ *
54
+ * A generated token embeds a server-issued `key` that is recovered from the
55
+ * verified credentials as `remote_user.token.key`. This middleware looks that
56
+ * key up in storage and rejects the request when the token:
57
+ * - is missing from storage (revoked or never issued);
58
+ * - is used from a client address outside its `cidr` whitelist;
59
+ * - is `readonly` and the request uses a write method (`DELETE`/`PATCH`/`POST`/`PUT`).
60
+ *
61
+ * Requests that do not carry a generated token key (e.g. interactive logins)
62
+ * pass through untouched. It must run after the JWT/auth middleware has
63
+ * populated `remote_user`. Failures fail closed: a storage lookup error yields
64
+ * an internal error rather than allowing the request.
65
+ *
66
+ * @param storage storage exposing {@link TokenReadableStorage.readTokens} to load the user's tokens
67
+ * @param logger logger used to record rejected attempts and lookup failures
68
+ * @returns an Express {@link RequestHandler} that calls `next()` to allow or `next(error)` to reject
69
+ */
70
+ function enforceGeneratedTokenMetadata(storage, logger) {
71
+ return async function (req, _res, next) {
72
+ const remoteUser = req.remote_user;
73
+ const tokenKey = remoteUser?.token?.key;
74
+ if (!tokenKey) {
75
+ return next();
76
+ }
77
+ const user = remoteUser?.name;
78
+ if (typeof user !== 'string') {
79
+ return next(_core.errorUtils.getForbidden(_core.API_ERROR.UNAUTHORIZED_ACCESS));
80
+ }
81
+ try {
82
+ const token = findToken(await storage.readTokens({
83
+ user
84
+ }), tokenKey);
85
+ if (!token) {
86
+ logger.warn({
87
+ tokenKey,
88
+ user
89
+ }, 'generated token @{tokenKey} for user @{user} is missing');
90
+ return next(_core.errorUtils.getForbidden(_core.API_ERROR.UNAUTHORIZED_ACCESS));
91
+ }
92
+ if (!_core.ipUtils.isAddressAllowed(getClientAddress(req), token.cidr)) {
93
+ logger.warn({
94
+ tokenKey,
95
+ user
96
+ }, 'generated token @{tokenKey} for user @{user} was used outside its CIDR whitelist');
97
+ return next(_core.errorUtils.getForbidden(_core.API_ERROR.UNAUTHORIZED_ACCESS));
98
+ }
99
+ if (token.readonly && WRITE_METHODS.has(req.method)) {
100
+ logger.warn({
101
+ tokenKey,
102
+ user
103
+ }, 'readonly generated token @{tokenKey} for user @{user} was used for a write request');
104
+ return next(_core.errorUtils.getForbidden(_core.API_ERROR.UNAUTHORIZED_ACCESS));
105
+ }
106
+ return next();
107
+ } catch (error) {
108
+ logger.error({
109
+ error: error.msg
110
+ }, 'generated token metadata lookup failed: @{error}');
111
+ return next(_core.errorUtils.getInternalError(error.message));
112
+ }
113
+ };
114
+ }
115
+ //# sourceMappingURL=token-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-auth.js","names":["_core","require","WRITE_METHODS","Set","getClientAddress","req","ipUtils","normalizeAddress","ip","socket","remoteAddress","findToken","tokens","tokenKey","find","key","enforceGeneratedTokenMetadata","storage","logger","_res","next","remoteUser","remote_user","token","user","name","errorUtils","getForbidden","API_ERROR","UNAUTHORIZED_ACCESS","readTokens","warn","isAddressAllowed","cidr","readonly","has","method","error","msg","getInternalError","message"],"sources":["../../src/middlewares/token-auth.ts"],"sourcesContent":["import type { NextFunction, Request, RequestHandler, Response } from 'express';\n\nimport { API_ERROR, errorUtils, ipUtils } from '@verdaccio/core';\nimport type { Logger, Token } from '@verdaccio/types';\n\nimport type { $RequestExtend } from '../types';\n\nconst WRITE_METHODS = new Set(['DELETE', 'PATCH', 'POST', 'PUT']);\n\n/**\n * Minimal storage surface required to enforce generated token metadata. Typed\n * structurally so this package does not need to depend on `@verdaccio/store`.\n */\nexport interface TokenReadableStorage {\n /**\n * Load the generated tokens stored for a user.\n *\n * @param filter narrows the lookup to a single `user`\n * @returns the user's persisted tokens\n */\n readTokens(filter: { user: string }): Promise<Token[]>;\n}\n\n/**\n * Resolve the client address of an incoming request for CIDR matching.\n *\n * Uses Express' `req.ip`, which already honors the application's `trust proxy`\n * setting (`config.server.trustProxy`): when the operator has declared trusted\n * proxies, `req.ip` is the upstream-most untrusted address parsed from\n * `X-Forwarded-For`; otherwise it is the direct socket peer and the forwarded\n * header is ignored entirely.\n *\n * The middleware must NOT read `X-Forwarded-For` on its own — doing so\n * unconditionally would let any client spoof its address (and bypass a token's\n * CIDR whitelist) simply by sending the header. Falls back to the raw socket\n * address and is normalized (trimmed, IPv4-mapped IPv6 prefix stripped) via\n * {@link ipUtils.normalizeAddress}.\n *\n * @param req the incoming Express request\n * @returns the normalized client address, or `undefined` when none can be determined\n */\nfunction getClientAddress(req: Request): string | undefined {\n return ipUtils.normalizeAddress(req.ip || req.socket.remoteAddress);\n}\n\n/**\n * Find the stored token whose `key` matches the key carried by the request.\n *\n * @param tokens the tokens persisted for the authenticated user\n * @param tokenKey the key extracted from the presented generated token\n * @returns the matching {@link Token}, or `undefined` when it has been revoked / never existed\n */\nfunction findToken(tokens: Token[], tokenKey: string): Token | undefined {\n return tokens.find(({ key }) => key === tokenKey);\n}\n\n/**\n * Express middleware that enforces the metadata of npm-style generated tokens\n * (created via `POST /-/npm/v1/tokens`).\n *\n * A generated token embeds a server-issued `key` that is recovered from the\n * verified credentials as `remote_user.token.key`. This middleware looks that\n * key up in storage and rejects the request when the token:\n * - is missing from storage (revoked or never issued);\n * - is used from a client address outside its `cidr` whitelist;\n * - is `readonly` and the request uses a write method (`DELETE`/`PATCH`/`POST`/`PUT`).\n *\n * Requests that do not carry a generated token key (e.g. interactive logins)\n * pass through untouched. It must run after the JWT/auth middleware has\n * populated `remote_user`. Failures fail closed: a storage lookup error yields\n * an internal error rather than allowing the request.\n *\n * @param storage storage exposing {@link TokenReadableStorage.readTokens} to load the user's tokens\n * @param logger logger used to record rejected attempts and lookup failures\n * @returns an Express {@link RequestHandler} that calls `next()` to allow or `next(error)` to reject\n */\nexport function enforceGeneratedTokenMetadata(\n storage: TokenReadableStorage,\n logger: Logger\n): RequestHandler {\n return async function (req: Request, _res: Response, next: NextFunction): Promise<void> {\n const remoteUser = (req as $RequestExtend).remote_user;\n const tokenKey = remoteUser?.token?.key;\n\n if (!tokenKey) {\n return next();\n }\n\n const user = remoteUser?.name;\n\n if (typeof user !== 'string') {\n return next(errorUtils.getForbidden(API_ERROR.UNAUTHORIZED_ACCESS));\n }\n\n try {\n const token = findToken(await storage.readTokens({ user }), tokenKey);\n\n if (!token) {\n logger.warn({ tokenKey, user }, 'generated token @{tokenKey} for user @{user} is missing');\n return next(errorUtils.getForbidden(API_ERROR.UNAUTHORIZED_ACCESS));\n }\n\n if (!ipUtils.isAddressAllowed(getClientAddress(req), token.cidr)) {\n logger.warn(\n { tokenKey, user },\n 'generated token @{tokenKey} for user @{user} was used outside its CIDR whitelist'\n );\n return next(errorUtils.getForbidden(API_ERROR.UNAUTHORIZED_ACCESS));\n }\n\n if (token.readonly && WRITE_METHODS.has(req.method)) {\n logger.warn(\n { tokenKey, user },\n 'readonly generated token @{tokenKey} for user @{user} was used for a write request'\n );\n return next(errorUtils.getForbidden(API_ERROR.UNAUTHORIZED_ACCESS));\n }\n\n return next();\n } catch (error: any) {\n logger.error({ error: error.msg }, 'generated token metadata lookup failed: @{error}');\n return next(errorUtils.getInternalError(error.message));\n }\n };\n}\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verdaccio/middleware",
3
- "version": "8.0.2",
3
+ "version": "8.0.4",
4
4
  "description": "Verdaccio Express Middleware",
5
5
  "main": "./build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -33,9 +33,9 @@
33
33
  "node": ">=18"
34
34
  },
35
35
  "dependencies": {
36
- "@verdaccio/config": "8.1.1",
37
- "@verdaccio/core": "8.1.1",
38
- "@verdaccio/url": "13.0.2",
36
+ "@verdaccio/config": "8.1.2",
37
+ "@verdaccio/core": "8.1.2",
38
+ "@verdaccio/url": "13.0.3",
39
39
  "debug": "4.4.3",
40
40
  "express": "4.22.1",
41
41
  "express-rate-limit": "5.5.1",
@@ -47,8 +47,8 @@
47
47
  "url": "https://opencollective.com/verdaccio"
48
48
  },
49
49
  "devDependencies": {
50
- "@verdaccio/logger": "8.0.2",
51
- "@verdaccio/types": "13.0.1",
50
+ "@verdaccio/logger": "8.0.3",
51
+ "@verdaccio/types": "13.0.2",
52
52
  "http-errors": "2.0.1",
53
53
  "supertest": "7.1.4",
54
54
  "jsdom": "26.1.0",