CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
index.mjs987 linesDownload Raw Back to dist
1// source/ip-key-generator.ts2import { isIPv6 } from "node:net";3import { Address6 } from "ip-address";4function ipKeyGenerator(ip, ipv6Subnet = 56) {5  if (isIPv6(ip)) {6    const address = new Address6(ip);7    if (address.is4()) return address.to4().correctForm();8    if (ipv6Subnet) {9      const subnet = new Address6(`${ip}/${ipv6Subnet}`);10      return subnet.networkForm();11    }12  }13  return ip;14}15 16// source/memory-store.ts17var MemoryStore = class {18  constructor(validations2) {19    this.validations = validations2;20    /**21     * These two maps store usage (requests) and reset time by key (for example, IP22     * addresses or API keys).23     *24     * They are split into two to avoid having to iterate through the entire set to25     * determine which ones need reset. Instead, `Client`s are moved from `previous`26     * to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients27     * left in `previous`, i.e., those that have not made any recent requests, are28     * known to be expired and can be deleted in bulk.29     */30    this.previous = /* @__PURE__ */ new Map();31    this.current = /* @__PURE__ */ new Map();32    /**33     * Confirmation that the keys incremented in once instance of MemoryStore34     * cannot affect other instances.35     */36    this.localKeys = true;37  }38  /**39   * Method that initializes the store.40   *41   * @param options {Options} - The options used to setup the middleware.42   */43  init(options) {44    this.windowMs = options.windowMs;45    this.validations?.windowMs(this.windowMs);46    if (this.interval) clearInterval(this.interval);47    this.interval = setInterval(() => {48      this.clearExpired();49    }, this.windowMs);50    this.interval.unref?.();51  }52  /**53   * Method to fetch a client's hit count and reset time.54   *55   * @param key {string} - The identifier for a client.56   *57   * @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.58   *59   * @public60   */61  async get(key) {62    return this.current.get(key) ?? this.previous.get(key);63  }64  /**65   * Method to increment a client's hit counter.66   *67   * @param key {string} - The identifier for a client.68   *69   * @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.70   *71   * @public72   */73  async increment(key) {74    const client = this.getClient(key);75    const now = Date.now();76    if (client.resetTime.getTime() <= now) {77      this.resetClient(client, now);78    }79    client.totalHits++;80    return client;81  }82  /**83   * Method to decrement a client's hit counter.84   *85   * @param key {string} - The identifier for a client.86   *87   * @public88   */89  async decrement(key) {90    const client = this.getClient(key);91    if (client.totalHits > 0) client.totalHits--;92  }93  /**94   * Method to reset a client's hit counter.95   *96   * @param key {string} - The identifier for a client.97   *98   * @public99   */100  async resetKey(key) {101    this.current.delete(key);102    this.previous.delete(key);103  }104  /**105   * Method to reset everyone's hit counter.106   *107   * @public108   */109  async resetAll() {110    this.current.clear();111    this.previous.clear();112  }113  /**114   * Method to stop the timer (if currently running) and prevent any memory115   * leaks.116   *117   * @public118   */119  shutdown() {120    clearInterval(this.interval);121    void this.resetAll();122  }123  /**124   * Recycles a client by setting its hit count to zero, and reset time to125   * `windowMs` milliseconds from now.126   *127   * NOT to be confused with `#resetKey()`, which removes a client from both the128   * `current` and `previous` maps.129   *130   * @param client {Client} - The client to recycle.131   * @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.132   *133   * @return {Client} - The modified client that was passed in, to allow for chaining.134   */135  resetClient(client, now = Date.now()) {136    client.totalHits = 0;137    client.resetTime.setTime(now + this.windowMs);138    return client;139  }140  /**141   * Retrieves or creates a client, given a key. Also ensures that the client being142   * returned is in the `current` map.143   *144   * @param key {string} - The key under which the client is (or is to be) stored.145   *146   * @returns {Client} - The requested client.147   */148  getClient(key) {149    if (this.current.has(key)) return this.current.get(key);150    let client;151    if (this.previous.has(key)) {152      client = this.previous.get(key);153      this.previous.delete(key);154    } else {155      client = { totalHits: 0, resetTime: /* @__PURE__ */ new Date() };156      this.resetClient(client);157    }158    this.current.set(key, client);159    return client;160  }161  /**162   * Move current clients to previous, create a new map for current.163   *164   * This function is called every `windowMs`.165   */166  clearExpired() {167    this.previous = this.current;168    this.current = /* @__PURE__ */ new Map();169  }170};171 172// source/rate-limit.ts173import { isIPv6 as isIPv62 } from "node:net";174 175// source/console-logger.ts176var ConsoleLogger = {177  warn(...args) {178    console.warn(...args.reverse());179  },180  error(...args) {181    console.error(...args.reverse());182  }183};184 185// source/headers.ts186import { Buffer } from "node:buffer";187import { createHash } from "node:crypto";188var SUPPORTED_DRAFT_VERSIONS = [189  "draft-6",190  "draft-7",191  "draft-8"192];193var getResetSeconds = (windowMs, resetTime) => {194  let resetSeconds;195  if (resetTime) {196    const deltaSeconds = Math.ceil((resetTime.getTime() - Date.now()) / 1e3);197    resetSeconds = Math.max(0, deltaSeconds);198  } else {199    resetSeconds = Math.ceil(windowMs / 1e3);200  }201  return resetSeconds;202};203var getPartitionKey = (key) => {204  const hash = createHash("sha256");205  hash.update(key);206  const partitionKey = hash.digest("hex").slice(0, 12);207  return Buffer.from(partitionKey).toString("base64");208};209var setLegacyHeaders = (response, info) => {210  if (response.headersSent) return;211  response.setHeader("X-RateLimit-Limit", info.limit.toString());212  response.setHeader("X-RateLimit-Remaining", info.remaining.toString());213  if (info.resetTime instanceof Date) {214    response.setHeader("Date", (/* @__PURE__ */ new Date()).toUTCString());215    response.setHeader(216      "X-RateLimit-Reset",217      Math.ceil(info.resetTime.getTime() / 1e3).toString()218    );219  }220};221var setDraft6Headers = (response, info, windowMs) => {222  if (response.headersSent) return;223  const windowSeconds = Math.ceil(windowMs / 1e3);224  const resetSeconds = getResetSeconds(windowMs, info.resetTime);225  response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);226  response.setHeader("RateLimit-Limit", info.limit.toString());227  response.setHeader("RateLimit-Remaining", info.remaining.toString());228  if (typeof resetSeconds === "number")229    response.setHeader("RateLimit-Reset", resetSeconds.toString());230};231var setDraft7Headers = (response, info, windowMs) => {232  if (response.headersSent) return;233  const windowSeconds = Math.ceil(windowMs / 1e3);234  const resetSeconds = getResetSeconds(windowMs, info.resetTime);235  response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);236  response.setHeader(237    "RateLimit",238    `limit=${info.limit}, remaining=${info.remaining}, reset=${resetSeconds}`239  );240};241var setDraft8Headers = (response, info, windowMs, name, key) => {242  if (response.headersSent) return;243  const windowSeconds = Math.ceil(windowMs / 1e3);244  const resetSeconds = getResetSeconds(windowMs, info.resetTime);245  const partitionKey = getPartitionKey(key);246  const header = `r=${info.remaining}; t=${resetSeconds}`;247  const policy = `q=${info.limit}; w=${windowSeconds}; pk=:${partitionKey}:`;248  response.append("RateLimit", `"${name}"; ${header}`);249  response.append("RateLimit-Policy", `"${name}"; ${policy}`);250};251var setRetryAfterHeader = (response, info, windowMs) => {252  if (response.headersSent) return;253  const resetSeconds = getResetSeconds(windowMs, info.resetTime);254  response.setHeader("Retry-After", resetSeconds.toString());255};256 257// source/utils.ts258var omitUndefinedProperties = (passedOptions) => {259  const omittedOptions = {};260  for (const k of Object.keys(passedOptions)) {261    const key = k;262    if (passedOptions[key] !== void 0) {263      omittedOptions[key] = passedOptions[key];264    }265  }266  return omittedOptions;267};268 269// source/validations.ts270import { isIP } from "node:net";271var ValidationError = class extends Error {272  /**273   * The code must be a string, in snake case and all capital, that starts with274   * the substring `ERR_ERL_`.275   *276   * The message must be a string, starting with an uppercase character,277   * describing the issue in detail.278   */279  constructor(code, message) {280    const url = `https://express-rate-limit.github.io/${code}/`;281    super(`${message} See ${url} for more information.`);282    this.name = this.constructor.name;283    this.code = code;284    this.help = url;285  }286};287var ChangeWarning = class extends ValidationError {288};289var usedStores = /* @__PURE__ */ new Set();290var singleCountKeys = /* @__PURE__ */ new WeakMap();291var validations = {292  enabled: {293    default: true294  },295  // Should be EnabledValidations type, but that's a circular reference296  disable() {297    for (const k of Object.keys(this.enabled)) this.enabled[k] = false;298  },299  /**300   * Checks whether the IP address is valid, and that it does not have a port301   * number in it.302   *303   * See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.304   *305   * @param ip {string | undefined} - The IP address provided by Express as request.ip.306   *307   * @returns {void}308   */309  ip(ip) {310    if (ip === void 0) {311      throw new ValidationError(312        "ERR_ERL_UNDEFINED_IP_ADDRESS",313        `An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.`314      );315    }316    if (!isIP(ip)) {317      throw new ValidationError(318        "ERR_ERL_INVALID_IP_ADDRESS",319        `An invalid 'request.ip' (${ip}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`320      );321    }322  },323  /**324   * Makes sure the trust proxy setting is not set to `true`.325   *326   * See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.327   *328   * @param request {Request} - The Express request object.329   *330   * @returns {void}331   */332  trustProxy(request) {333    if (request.app.get("trust proxy") === true) {334      throw new ValidationError(335        "ERR_ERL_PERMISSIVE_TRUST_PROXY",336        `The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.`337      );338    }339  },340  /**341   * Makes sure the trust proxy setting is set in case the `X-Forwarded-For`342   * header is present.343   *344   * See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.345   *346   * @param request {Request} - The Express request object.347   *348   * @returns {void}349   */350  xForwardedForHeader(request) {351    if (request.headers["x-forwarded-for"] && request.app.get("trust proxy") === false) {352      throw new ValidationError(353        "ERR_ERL_UNEXPECTED_X_FORWARDED_FOR",354        `The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.`355      );356    }357  },358  /**359   * Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)360   *361   * @param request {Request} - The Express request object.362   *363   * @returns {void}364   */365  forwardedHeader(request) {366    if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {367      throw new ValidationError(368        "ERR_ERL_FORWARDED_HEADER",369        `The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`370      );371    }372  },373  /**374   * Ensures totalHits value from store is a positive integer.375   *376   * @param hits {any} - The `totalHits` returned by the store.377   */378  positiveHits(hits) {379    if (typeof hits !== "number" || hits < 1 || hits !== Math.round(hits)) {380      throw new ValidationError(381        "ERR_ERL_INVALID_HITS",382        `The totalHits value returned from the store must be a positive integer, got ${hits}`383      );384    }385  },386  /**387   * Ensures a single store instance is not used with multiple express-rate-limit instances388   */389  unsharedStore(store) {390    if (usedStores.has(store)) {391      const maybeUniquePrefix = store?.localKeys ? "" : " (with a unique prefix)";392      throw new ValidationError(393        "ERR_ERL_STORE_REUSE",394        `A Store instance must not be shared across multiple rate limiters. Create a new instance of ${store.constructor.name}${maybeUniquePrefix} for each limiter instead.`395      );396    }397    usedStores.add(store);398  },399  /**400   * Ensures a given key is incremented only once per request.401   *402   * @param request {Request} - The Express request object.403   * @param store {Store} - The store class.404   * @param key {string} - The key used to store the client's hit count.405   *406   * @returns {void}407   */408  singleCount(request, store, key) {409    let storeKeys = singleCountKeys.get(request);410    if (!storeKeys) {411      storeKeys = /* @__PURE__ */ new Map();412      singleCountKeys.set(request, storeKeys);413    }414    const storeKey = store.localKeys ? store : store.constructor.name;415    let keys = storeKeys.get(storeKey);416    if (!keys) {417      keys = [];418      storeKeys.set(storeKey, keys);419    }420    const prefixedKey = `${store.prefix ?? ""}${key}`;421    if (keys.includes(prefixedKey)) {422      throw new ValidationError(423        "ERR_ERL_DOUBLE_COUNT",424        `The hit count for ${key} was incremented more than once for a single request.`425      );426    }427    keys.push(prefixedKey);428  },429  /**430   * Warns the user that the behaviour for `max: 0` / `limit: 0` is431   * changing in the next major release.432   *433   * @param limit {number} - The maximum number of hits per client.434   *435   * @returns {void}436   */437  limit(limit) {438    if (limit === 0) {439      throw new ChangeWarning(440        "WRN_ERL_MAX_ZERO",441        "Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7"442      );443    }444  },445  /**446   * Warns the user that the `draft_polli_ratelimit_headers` option is deprecated447   * and will be removed in the next major release.448   *449   * @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.450   *451   * @returns {void}452   */453  draftPolliHeaders(draft_polli_ratelimit_headers) {454    if (draft_polli_ratelimit_headers) {455      throw new ChangeWarning(456        "WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS",457        `The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.`458      );459    }460  },461  /**462   * Warns the user that the `onLimitReached` option is deprecated and463   * will be removed in the next major release.464   *465   * @param onLimitReached {any | undefined} - The maximum number of hits per client.466   *467   * @returns {void}468   */469  onLimitReached(onLimitReached) {470    if (onLimitReached) {471      throw new ChangeWarning(472        "WRN_ERL_DEPRECATED_ON_LIMIT_REACHED",473        "The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7."474      );475    }476  },477  /**478   * Warns the user when an invalid/unsupported version of the draft spec is passed.479   *480   * @param version {any | undefined} - The version passed by the user.481   *482   * @returns {void}483   */484  headersDraftVersion(version) {485    if (typeof version !== "string" || // @ts-expect-error This is fine. If version is not in the array, it will just return false.486    !SUPPORTED_DRAFT_VERSIONS.includes(version)) {487      const versionString = SUPPORTED_DRAFT_VERSIONS.join(", ");488      throw new ValidationError(489        "ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION",490        `standardHeaders: only the following versions of the IETF draft specification are supported: ${versionString}.`491      );492    }493  },494  /**495   * Warns the user when the selected headers option requires a reset time but496   * the store does not provide one.497   *498   * @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.499   *500   * @returns {void}501   */502  headersResetTime(resetTime) {503    if (!resetTime) {504      throw new ValidationError(505        "ERR_ERL_HEADERS_NO_RESET",506        `standardHeaders:  'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.`507      );508    }509  },510  knownOptions(passedOptions) {511    if (!passedOptions) return;512    const optionsMap = {513      windowMs: true,514      limit: true,515      message: true,516      statusCode: true,517      legacyHeaders: true,518      standardHeaders: true,519      identifier: true,520      requestPropertyName: true,521      skipFailedRequests: true,522      skipSuccessfulRequests: true,523      keyGenerator: true,524      ipv6Subnet: true,525      handler: true,526      skip: true,527      requestWasSuccessful: true,528      store: true,529      validate: true,530      headers: true,531      max: true,532      passOnStoreError: true,533      logger: true534    };535    const validOptions = Object.keys(optionsMap).concat(536      "draft_polli_ratelimit_headers",537      // not a valid option anymore, but we have a more specific check for this one, so don't warn for it here538      // from express-slow-down - https://github.com/express-rate-limit/express-slow-down/blob/main/source/types.ts#L65539      "delayAfter",540      "delayMs",541      "maxDelayMs"542    );543    for (const key of Object.keys(passedOptions)) {544      if (!validOptions.includes(key)) {545        throw new ValidationError(546          "ERR_ERL_UNKNOWN_OPTION",547          `Unexpected configuration option: ${key}`548          // todo: suggest a valid option with a short levenstein distance?549        );550      }551    }552  },553  /**554   * Checks the options.validate setting to ensure that only recognized555   * validations are enabled or disabled.556   *557   * If any unrecognized values are found, an error is logged that558   * includes the list of supported validations.559   */560  validationsConfig() {561    const supportedValidations = Object.keys(this).filter(562      (k) => !["enabled", "disable"].includes(k)563    );564    supportedValidations.push("default");565    for (const key of Object.keys(this.enabled)) {566      if (!supportedValidations.includes(key)) {567        throw new ValidationError(568          "ERR_ERL_UNKNOWN_VALIDATION",569          `options.validate.${key} is not recognized. Supported validate options are: ${supportedValidations.join(570            ", "571          )}.`572        );573      }574    }575  },576  /**577   * Checks to see if the instance was created inside of a request handler,578   * which would prevent it from working correctly, with the default memory579   * store (or any other store with localKeys.)580   */581  creationStack(store) {582    const { stack } = new Error(583      "express-rate-limit validation check (set options.validate.creationStack=false to disable)"584    );585    if (stack?.includes("Layer.handle [as handle_request]") || // express v4586    stack?.includes("Layer.handleRequest")) {587      if (!store.localKeys) {588        throw new ValidationError(589          "ERR_ERL_CREATED_IN_REQUEST_HANDLER",590          "express-rate-limit instance should *usually* be created at app initialization, not when responding to a request."591        );592      }593      throw new ValidationError(594        "ERR_ERL_CREATED_IN_REQUEST_HANDLER",595        "express-rate-limit instance should be created at app initialization, not when responding to a request."596      );597    }598  },599  ipv6Subnet(ipv6Subnet) {600    if (ipv6Subnet === false) {601      return;602    }603    if (!Number.isInteger(ipv6Subnet) || ipv6Subnet < 32 || ipv6Subnet > 64) {604      throw new ValidationError(605        "ERR_ERL_IPV6_SUBNET",606        `Unexpected ipv6Subnet value: ${ipv6Subnet}. Expected an integer between 32 and 64 (usually 48-64).`607      );608    }609  },610  ipv6SubnetOrKeyGenerator(options) {611    if (options.ipv6Subnet !== void 0 && options.keyGenerator) {612      throw new ValidationError(613        "ERR_ERL_IPV6SUBNET_OR_KEYGENERATOR",614        `Incompatible options: the 'ipv6Subnet' option is ignored when a custom 'keyGenerator' function is also set.`615      );616    }617  },618  keyGeneratorIpFallback(keyGenerator) {619    if (!keyGenerator) {620      return;621    }622    const src = keyGenerator.toString();623    if ((src.includes("req.ip") || src.includes("request.ip")) && !src.includes("ipKeyGenerator")) {624      throw new ValidationError(625        "ERR_ERL_KEY_GEN_IPV6",626        "Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."627      );628    }629  },630  /**631   * Checks to see if the window duration is greater than 2^32 - 1. This is only632   * called by the default MemoryStore, since it uses Node's setInterval method.633   *634   * See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.635   */636  windowMs(windowMs) {637    const SET_TIMEOUT_MAX = 2 ** 31 - 1;638    if (typeof windowMs !== "number" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {639      throw new ValidationError(640        "ERR_ERL_WINDOW_MS",641        `Invalid windowMs value: ${windowMs}${typeof windowMs !== "number" ? ` (${typeof windowMs})` : ""}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`642      );643    }644  }645};646function validateLogger(logger) {647  if (typeof logger !== "object" || typeof logger.error !== "function" || typeof logger.warn !== "function") {648    throw new TypeError(649      "Provided logger does not implement the Logger interface"650    );651  }652}653var getValidations = (_enabled, logger) => {654  validateLogger(logger);655  let enabled;656  if (typeof _enabled === "boolean") {657    enabled = {658      default: _enabled659    };660  } else {661    enabled = {662      default: true,663      ..._enabled664    };665  }666  const wrappedValidations = { enabled };667  for (const [name, validation] of Object.entries(validations)) {668    if (typeof validation === "function")669      wrappedValidations[name] = (...args) => {670        if (!(enabled[name] ?? enabled.default)) {671          return;672        }673        try {674          ;675          validation.apply(676            wrappedValidations,677            args678          );679        } catch (error) {680          if (error instanceof ChangeWarning) logger.warn(error);681          else logger.error(error);682        }683      };684  }685  return wrappedValidations;686};687 688// source/rate-limit.ts689var isLegacyStore = (store) => (690  // Check that `incr` exists but `increment` does not - store authors might want691  // to keep both around for backwards compatibility.692  typeof store.incr === "function" && typeof store.increment !== "function"693);694var promisifyStore = (passedStore) => {695  if (!isLegacyStore(passedStore)) {696    return passedStore;697  }698  const legacyStore = passedStore;699  class PromisifiedStore {700    async increment(key) {701      return new Promise((resolve, reject) => {702        legacyStore.incr(703          key,704          (error, totalHits, resetTime) => {705            if (error) reject(error);706            resolve({ totalHits, resetTime });707          }708        );709      });710    }711    async decrement(key) {712      return legacyStore.decrement(key);713    }714    async resetKey(key) {715      return legacyStore.resetKey(key);716    }717    /* istanbul ignore next */718    async resetAll() {719      if (typeof legacyStore.resetAll === "function")720        return legacyStore.resetAll();721    }722  }723  return new PromisifiedStore();724};725var getOptionsFromConfig = (config) => {726  const { validations: validations2, ...directlyPassableEntries } = config;727  return {728    ...directlyPassableEntries,729    validate: validations2.enabled730  };731};732var parseOptions = (passedOptions) => {733  const notUndefinedOptions = omitUndefinedProperties(passedOptions);734  const logger = passedOptions.logger ?? ConsoleLogger;735  const validations2 = getValidations(736    notUndefinedOptions?.validate ?? true,737    logger738  );739  validations2.validationsConfig();740  validations2.knownOptions(passedOptions);741  validations2.draftPolliHeaders(742    // @ts-expect-error see the note above.743    notUndefinedOptions.draft_polli_ratelimit_headers744  );745  validations2.onLimitReached(notUndefinedOptions.onLimitReached);746  if (notUndefinedOptions.ipv6Subnet !== void 0 && typeof notUndefinedOptions.ipv6Subnet !== "function") {747    validations2.ipv6Subnet(notUndefinedOptions.ipv6Subnet);748  }749  validations2.keyGeneratorIpFallback(notUndefinedOptions.keyGenerator);750  validations2.ipv6SubnetOrKeyGenerator(notUndefinedOptions);751  let standardHeaders = notUndefinedOptions.standardHeaders ?? false;752  if (standardHeaders === true) standardHeaders = "draft-6";753  const config = {754    windowMs: 60 * 1e3,755    limit: passedOptions.max ?? 5,756    // `max` is deprecated, but support it anyways.757    message: "Too many requests, please try again later.",758    statusCode: 429,759    legacyHeaders: passedOptions.headers ?? true,760    identifier(request, _response) {761      let duration = "";762      const property = config.requestPropertyName;763      const { limit } = request[property];764      const seconds = config.windowMs / 1e3;765      const minutes = config.windowMs / (1e3 * 60);766      const hours = config.windowMs / (1e3 * 60 * 60);767      const days = config.windowMs / (1e3 * 60 * 60 * 24);768      if (seconds < 60) duration = `${seconds}sec`;769      else if (minutes < 60) duration = `${minutes}min`;770      else if (hours < 24) duration = `${hours}hr${hours > 1 ? "s" : ""}`;771      else duration = `${days}day${days > 1 ? "s" : ""}`;772      return `${limit}-in-${duration}`;773    },774    requestPropertyName: "rateLimit",775    skipFailedRequests: false,776    skipSuccessfulRequests: false,777    requestWasSuccessful: (_request, response) => response.statusCode < 400,778    skip: (_request, _response) => false,779    async keyGenerator(request, response) {780      validations2.ip(request.ip);781      validations2.trustProxy(request);782      validations2.xForwardedForHeader(request);783      validations2.forwardedHeader(request);784      const ip = request.ip;785      let subnet = 56;786      if (isIPv62(ip)) {787        subnet = typeof config.ipv6Subnet === "function" ? await config.ipv6Subnet(request, response) : config.ipv6Subnet;788        if (typeof config.ipv6Subnet === "function")789          validations2.ipv6Subnet(subnet);790      }791      return ipKeyGenerator(ip, subnet);792    },793    ipv6Subnet: 56,794    async handler(request, response, _next, _optionsUsed) {795      response.status(config.statusCode);796      const message = typeof config.message === "function" ? await config.message(797        request,798        response799      ) : config.message;800      if (!response.writableEnded) response.send(message);801    },802    passOnStoreError: false,803    // Allow the default options to be overridden by the passed options.804    ...notUndefinedOptions,805    // `standardHeaders` is resolved into a draft version above, use that.806    standardHeaders,807    // Note that this field is declared after the user's options are spread in,808    // so that this field doesn't get overridden with an un-promisified store!809    store: promisifyStore(810      notUndefinedOptions.store ?? new MemoryStore(validations2)811    ),812    // Print an error to the console if a few known misconfigurations are detected.813    validations: validations2,814    logger815  };816  if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || config.store.resetAll !== void 0 && typeof config.store.resetAll !== "function" || config.store.init !== void 0 && typeof config.store.init !== "function") {817    throw new TypeError(818      "An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface."819    );820  }821  return config;822};823var handleAsyncErrors = (fn) => async (request, response, next) => {824  try {825    await Promise.resolve(fn(request, response, next)).catch(next);826  } catch (error) {827    next(error);828  }829};830var rateLimit = (passedOptions) => {831  const config = parseOptions(passedOptions ?? {});832  const options = getOptionsFromConfig(config);833  config.validations.creationStack(config.store);834  config.validations.unsharedStore(config.store);835  if (typeof config.store.init === "function") {836    try {837      const storeInit = config.store.init(options);838      if (storeInit instanceof Promise) {839        storeInit.catch(840          (error) => config.logger.error(841            error,842            "express-rate-limit: async error during store initialization."843          )844        );845      }846    } catch (error) {847      config.logger.error(848        error,849        "express-rate-limit: error during store initialization."850      );851    }852  }853  const middleware = handleAsyncErrors(854    async (request, response, next) => {855      const closePromise = config.skipFailedRequests && new Promise((resolve) => response.once("close", resolve));856      const finishPromise = (config.skipFailedRequests || config.skipSuccessfulRequests) && new Promise((resolve) => response.once("finish", resolve));857      const errorPromise = config.skipFailedRequests && new Promise((resolve) => response.once("error", resolve));858      const skip = await config.skip(request, response);859      if (skip) {860        next();861        return;862      }863      const augmentedRequest = request;864      const key = await config.keyGenerator(request, response);865      let totalHits = 0;866      let resetTime;867      try {868        const incrementResult = await config.store.increment(key);869        totalHits = incrementResult.totalHits;870        resetTime = incrementResult.resetTime;871      } catch (error) {872        if (config.passOnStoreError) {873          config.logger.error(874            error,875            "express-rate-limit: error from store, allowing request without rate-limiting."876          );877          next();878          return;879        }880        throw error;881      }882      config.validations.positiveHits(totalHits);883      config.validations.singleCount(request, config.store, key);884      const retrieveLimit = typeof config.limit === "function" ? config.limit(request, response) : config.limit;885      const limit = await retrieveLimit;886      config.validations.limit(limit);887      const info = {888        limit,889        used: totalHits,890        remaining: Math.max(limit - totalHits, 0),891        resetTime,892        key893      };894      Object.defineProperty(info, "current", {895        configurable: false,896        enumerable: false,897        value: totalHits898      });899      augmentedRequest[config.requestPropertyName] = info;900      if (config.legacyHeaders && !response.headersSent) {901        setLegacyHeaders(response, info);902      }903      if (config.standardHeaders && !response.headersSent) {904        switch (config.standardHeaders) {905          case "draft-6": {906            setDraft6Headers(response, info, config.windowMs);907            break;908          }909          case "draft-7": {910            config.validations.headersResetTime(info.resetTime);911            setDraft7Headers(response, info, config.windowMs);912            break;913          }914          case "draft-8": {915            const retrieveName = typeof config.identifier === "function" ? config.identifier(request, response) : config.identifier;916            const name = await retrieveName;917            config.validations.headersResetTime(info.resetTime);918            setDraft8Headers(response, info, config.windowMs, name, key);919            break;920          }921          default: {922            config.validations.headersDraftVersion(config.standardHeaders);923            break;924          }925        }926      }927      if (config.skipFailedRequests || config.skipSuccessfulRequests) {928        let decremented = false;929        const decrementKey = async () => {930          if (!decremented) {931            await config.store.decrement(key);932            decremented = true;933          }934        };935        if (config.skipFailedRequests) {936          if (finishPromise) {937            void finishPromise.then(async () => {938              if (!await config.requestWasSuccessful(request, response))939                await decrementKey();940            });941          }942          if (closePromise) {943            void closePromise.then(async () => {944              if (!response.writableEnded) await decrementKey();945            });946          }947          if (errorPromise) {948            void errorPromise.then(async () => {949              await decrementKey();950            });951          }952        }953        if (config.skipSuccessfulRequests) {954          if (finishPromise) {955            void finishPromise.then(async () => {956              if (await config.requestWasSuccessful(request, response))957                await decrementKey();958            });959          }960        }961      }962      config.validations.disable();963      if (totalHits > limit) {964        if (config.legacyHeaders || config.standardHeaders) {965          setRetryAfterHeader(response, info, config.windowMs);966        }967        config.handler(request, response, next, options);968        return;969      }970      next();971    }972  );973  const getThrowFn = () => {974    throw new Error("The current store does not support the get/getKey method");975  };976  middleware.resetKey = config.store.resetKey.bind(config.store);977  middleware.getKey = typeof config.store.get === "function" ? config.store.get.bind(config.store) : getThrowFn;978  return middleware;979};980var rate_limit_default = rateLimit;981export {982  MemoryStore,983  rate_limit_default as default,984  ipKeyGenerator,985  rate_limit_default as rateLimit986};987 
basant307/AI_Governance_Project · CoolFace