diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 4b38d62..aed3860 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -151,9 +151,9 @@ "integrity": "sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==" }, "node_modules/@types/node-fetch": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.10.tgz", - "integrity": "sha512-PPpPK6F9ALFTn59Ka3BaL+qGuipRfxNE8qVgkp0bVixeiR2c2/L+IVOiBdu9JhhT22sWnQEp6YyHGI2b2+CMcA==", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", "dependencies": { "@types/node": "*", "form-data": "^4.0.0" @@ -444,6 +444,11 @@ "node": ">= 12" } }, + "node_modules/ddos": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ddos/-/ddos-0.2.1.tgz", + "integrity": "sha512-IDTS5NBK0PzbYWqXhZyIVkrKGdvC+lEfOUF8HWWDYdvH8pr4s0eQNDNgWmaSQhZWe8GXQcyqujdQXklML4d9LQ==" + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -536,9 +541,9 @@ } }, "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz", + "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==", "engines": { "node": ">=12" }, @@ -1087,9 +1092,9 @@ } }, "node_modules/nodemon": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.2.tgz", - "integrity": "sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz", + "integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==", "dependencies": { "chokidar": "^3.5.2", "debug": "^4", diff --git a/node_modules/@types/node-fetch/README.md b/node_modules/@types/node-fetch/README.md index f92b487..43bd5dc 100644 --- a/node_modules/@types/node-fetch/README.md +++ b/node_modules/@types/node-fetch/README.md @@ -8,7 +8,7 @@ This package contains type definitions for node-fetch (https://github.com/bitinn Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node-fetch. ### Additional Details - * Last updated: Wed, 27 Dec 2023 20:07:01 GMT + * Last updated: Tue, 16 Jan 2024 23:07:00 GMT * Dependencies: [@types/node](https://npmjs.com/package/@types/node), [form-data](https://npmjs.com/package/form-data) # Credits diff --git a/node_modules/@types/node-fetch/index.d.ts b/node_modules/@types/node-fetch/index.d.ts index 812e6f2..9000ab9 100644 --- a/node_modules/@types/node-fetch/index.d.ts +++ b/node_modules/@types/node-fetch/index.d.ts @@ -5,7 +5,7 @@ import { RequestOptions } from "http"; import { URL, URLSearchParams } from "url"; import { AbortSignal } from "./externals"; -export class Request extends Body { +declare class Request extends Body { constructor(input: RequestInfo, init?: RequestInit); clone(): Request; context: RequestContext; @@ -27,7 +27,7 @@ export class Request extends Body { timeout: number; } -export interface RequestInit { +interface RequestInit { // whatwg/fetch standard options body?: BodyInit | undefined; headers?: HeadersInit | undefined; @@ -45,7 +45,7 @@ export interface RequestInit { // node-fetch does not support mode, cache or credentials options } -export type RequestContext = +type RequestContext = | "audio" | "beacon" | "cspreport" @@ -79,11 +79,11 @@ export type RequestContext = | "worker" | "xmlhttprequest" | "xslt"; -export type RequestMode = "cors" | "no-cors" | "same-origin"; -export type RequestRedirect = "error" | "follow" | "manual"; -export type RequestCredentials = "omit" | "include" | "same-origin"; +type RequestMode = "cors" | "no-cors" | "same-origin"; +type RequestRedirect = "error" | "follow" | "manual"; +type RequestCredentials = "omit" | "include" | "same-origin"; -export type RequestCache = +type RequestCache = | "default" | "force-cache" | "no-cache" @@ -91,7 +91,7 @@ export type RequestCache = | "only-if-cached" | "reload"; -export class Headers implements Iterable<[string, string]> { +declare class Headers implements Iterable<[string, string]> { constructor(init?: HeadersInit); forEach(callback: (value: string, name: string) => void): void; append(name: string, value: string): void; @@ -115,7 +115,7 @@ interface BlobOptions { endings?: "transparent" | "native" | undefined; } -export class Blob { +declare class Blob { constructor(blobParts?: BlobPart[], options?: BlobOptions); readonly type: string; readonly size: number; @@ -123,7 +123,7 @@ export class Blob { text(): Promise; } -export class Body { +declare class Body { constructor(body?: any, opts?: { size?: number | undefined; timeout?: number | undefined }); arrayBuffer(): Promise; blob(): Promise; @@ -141,13 +141,13 @@ interface SystemError extends Error { code?: string | undefined; } -export class AbortError extends Error { +declare class AbortError extends Error { readonly name: "AbortError"; constructor(message: string); readonly type: "aborted"; } -export class FetchError extends Error { +declare class FetchError extends Error { name: "FetchError"; constructor(message: string, type: string, systemError?: SystemError); type: string; @@ -155,7 +155,7 @@ export class FetchError extends Error { errno?: string | undefined; } -export class Response extends Body { +declare class Response extends Body { constructor(body?: BodyInit, init?: ResponseInit); static error(): Response; static redirect(url: string, status: number): Response; @@ -169,7 +169,7 @@ export class Response extends Body { url: string; } -export type ResponseType = +type ResponseType = | "basic" | "cors" | "default" @@ -177,7 +177,7 @@ export type ResponseType = | "opaque" | "opaqueredirect"; -export interface ResponseInit { +interface ResponseInit { headers?: HeadersInit | undefined; size?: number | undefined; status?: number | undefined; @@ -191,17 +191,15 @@ interface URLLike { href: string; } -export type HeadersInit = Headers | string[][] | { [key: string]: string | string[] }; -// HeaderInit is exported to support backwards compatibility. See PR #34382 -export type HeaderInit = HeadersInit; -export type BodyInit = +type HeadersInit = Headers | string[][] | { [key: string]: string | string[] }; +type BodyInit = | ArrayBuffer | ArrayBufferView | NodeJS.ReadableStream | string | URLSearchParams | FormData; -export type RequestInfo = string | URLLike | Request; +type RequestInfo = string | URLLike | Request; declare function fetch( url: RequestInfo, @@ -209,7 +207,32 @@ declare function fetch( ): Promise; declare namespace fetch { - function isRedirect(code: number): boolean; + export { + AbortError, + Blob, + Body, + BodyInit, + FetchError, + Headers, + HeadersInit, + // HeaderInit is exported to support backwards compatibility. See PR #34382 + HeadersInit as HeaderInit, + Request, + RequestCache, + RequestContext, + RequestCredentials, + RequestInfo, + RequestInit, + RequestMode, + RequestRedirect, + Response, + ResponseInit, + ResponseType, + }; + export function isRedirect(code: number): boolean; + + import _default = fetch; + export { _default as default }; } -export default fetch; +export = fetch; diff --git a/node_modules/@types/node-fetch/package.json b/node_modules/@types/node-fetch/package.json index 7ae9de8..84884d5 100644 --- a/node_modules/@types/node-fetch/package.json +++ b/node_modules/@types/node-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@types/node-fetch", - "version": "2.6.10", + "version": "2.6.11", "description": "TypeScript definitions for node-fetch", "homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node-fetch", "license": "MIT", @@ -78,6 +78,6 @@ "@types/node": "*", "form-data": "^4.0.0" }, - "typesPublisherContentHash": "8e35ac0fe2beabdc31c18cc67d05ce2d6de0cb38e5d65ea6bf15ad18a13fc6ca", + "typesPublisherContentHash": "912e2c03935d0f960f529de6f688e52c77e78b7ca935198cd500804e69ea371f", "typeScriptVersion": "4.6" } \ No newline at end of file diff --git a/node_modules/ddos/.travis.yml b/node_modules/ddos/.travis.yml new file mode 100644 index 0000000..2bb501b --- /dev/null +++ b/node_modules/ddos/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - "8" +after_success: + - COVERALLS_REPO_TOKEN=$coveralls_repo_token npm run coverage \ No newline at end of file diff --git a/node_modules/ddos/LICENSE b/node_modules/ddos/LICENSE new file mode 100644 index 0000000..a8bdcd6 --- /dev/null +++ b/node_modules/ddos/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [2014-2017] David Wee + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/ddos/README.md b/node_modules/ddos/README.md new file mode 100644 index 0000000..4687f11 --- /dev/null +++ b/node_modules/ddos/README.md @@ -0,0 +1,300 @@ +Configurable Denial-Of-Service prevention for http services + + + +[![Build Status](https://travis-ci.org/rook2pawn/node-ddos.svg?branch=master)](https://travis-ci.org/rook2pawn/node-ddos) + +[![Coverage Status](https://coveralls.io/repos/github/rook2pawn/node-ddos/badge.svg?branch=master)](https://coveralls.io/github/rook2pawn/node-ddos?branch=master) + +# install + +``` + npm install --save ddos +``` + +# setup helper (new!) + +``` + npm run setup-helper +``` + +Run `npm run setup-helper` and place the console side by side with your browser window and reload a few times and see how `burst` and `limit` are separate +concepts. `burst` controls the expiry timer, and `limit` is what governs the actual denial. I made a [video tutorial](https://youtu.be/yx2T0oaF2T0) on this, which should +give you an intuitive sense of what's going on. Play with the limit and burst in the `setupHelper.js`. + + + +# A Quick Overview + +```js + var Ddos = require('ddos') + var express = require('express') + var ddos = new Ddos({burst:10, limit:15}) + var app = express(); + app.use(ddos.express); +``` + +* **Rule 1** Every request per user increments an internal **count**. When the count exceeds the **limit**, the requests are denied with a HTTP 429 Too Many Requests. + +* **Rule 2** The *only* way for count to go away, is for an internal expiration time to expire, called the **expiry**, and is measured in seconds. Every second, the expiry time will go down by one. + +The first request comes in and the expiry is set to 1 second. If 1 second passes and no additional requests are made, then the entry is removed +from the internal table. In fact, there can be up to **burst** amount of requests made and the **expiry time will not change**. +The only way the expiry goes up is when a request comes, the count goes up, and then if the count *exceeds* the burst amount (greater than, not greater than or equal to), then the expiry goes up to twice its previous value. + +Every time the table is checked (defaults to 1 second, configurable by the **checkinterval** setting), the expiry goes down by that amount of time. +Now we loop back to **Rule 2** when that when expiry is less than or equal to 0, then that entry is removed along with the count. + + +## Features + +[![Join the chat at https://gitter.im/rook2pawn/node-ddos](https://badges.gitter.im/rook2pawn/node-ddos.svg)](https://gitter.im/rook2pawn/node-ddos?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + + * support the X-Forwarded-For header in a reverse proxy request + +## Supports + + * HapiJS 17+ + * HapiJS 16 and before + * Express 4+ + * Koa + +### With [Express](https://github.com/expressjs/expressjs.com "Express") + +```js + var Ddos = require('ddos') + var express = require('express') + var ddos = new Ddos; + var app = express(); + app.use(ddos.express) +``` + +or with a router + +```js + const router = express.Router(); + + router.use(ddos.express); + router.get("/", (req,res,next) => { + console.log("Beep"); + res.end("Boop"); + }) + app.use(router); +``` +This way, all paths defined on the router will be protected. + +### With [HapiJS 17+](https://hapijs.com/ "HapiJS") + +```js + var Ddos = require('ddos') + var Hapi = require('hapi'); + + var ddos = new Ddos; + const server = Hapi.server({ + port: 3000, + host: "localhost" + }); + server.route({ + method: "GET", + path: "/", + handler: (request, h) => { + return "Hello, world!"; + } + }); + server.ext("onRequest", ddos.hapi17.bind(ddos)); + + server.start() + .then(() => { + + }) + +``` + +### With [HapiJS 16 and before](https://hapijs.com/ "HapiJS") + +```js + var Ddos = require('ddos') + var Hapi = require('hapi'); + + var ddos = new Ddos; + const server = new Hapi.Server(); + server.ext('onRequest', ddos.hapi.bind(ddos)); +``` + +### With [Koa](http://koajs.com "KoaJS") + +```js + var Ddos = require('ddos') + var koa = require('koa') + var ddos = new Ddos; + + var app = new koa; + app.use(ddos.koa().bind(ddos)) // be sure to bind ddos as koa rebinds the context +``` + +### With [Router-Middleware](https://github.com/rook2pawn/router-middleware "Router Middleware") + +```js + var Router = require('router-middleware'); + var Ddos = require('ddos') + + var ddos = new Ddos; + var app = Router(); + app.use(ddos); +``` + +## How does this ddos prevention module work? + +Every request marks the internal table and increments the `count`. +This is how an entry in the table managed by this module looks + + { host : , count: 1, expiry: 1 } + +When a second request is made + + { host : , count: 2, expiry: 1 } + +and the third + + { host : , count: 3, expiry: 1 } + +and so on. If the count exceeds the configurable **burst** amount, then the expiry goes up by twice the previous expiry, 1, 2, 4, 8, 16, etc. + +When count exceeds the **limit**, then the request is denied, otherwise, the request is permitted. + +Every time the internal table is checked, the expiration goes down by the time elapsed. + +The only way for a user who has denied requests to continue is for them to let the expiration time pass, and when expiration hits 0, the entry is deleted from the table, and new requests are allowed like normal. + +## Processing and Memory Usage by this module + +There is only ONE table, and within it only one small entry per IP, and that entry is transient and will be deleted within normal parameters. The table itself is combed over at the configurable **checkinterval** in seconds. + +## Yes, this will not deal with distributed denial-of-service attacks + +But it will deal with simple DOS ones, but the concept is associated with DDOS whereas DOS is about the classic operating system from the 90's. + + +## Let's review Configuration + +To override any configuration option, simply specify it at construction time. + +```js + var Ddos = require('ddos'); + var ddos = new Ddos({burst:3,limit:4,testmode:true,whitelist:['74.125.224.72']}); +``` + +Let's go over the configuration options to help illustrate how this module works. +All of the configurations default to the following: + + params.maxcount = 30; + params.burst = 5; + params.limit = _params.burst * 4; + params.maxexpiry = 120; + params.checkinterval = 1; + params.trustProxy = true; + params.includeUserAgent = true; + params.whitelist = []; + params.errormessage = 'Error'; + params.testmode = false; + params.responseStatus = 429; + +### testmode + +`testmode` allows you to see exactly how your setup is functioning. + +### limit + +`limit` is the number of maximum counts allowed (do not confuse that with maxcount). `count` increments with each request. +If the `count` exceeds the `limit`, then the request is denied. Recommended limit is to use a multiple of the number of bursts. + + +### maxcount + +When the `count` exceeds the `limit` and then the `maxcount`, the count is reduced to the `maxcount`. The maxcount is simply is the maximum amount of "punishment" that could be applied to a denial time-out. + + +### burst + +Burst is the number or amount of allowable burst requests before the client starts being penalized. +When the client is penalized, the expiration is increased by twice the previous expiration. + + +### maxexpiry + +maxexpiry is the seconds of maximum amount of expiration time. +In order for the user to use whatever service you are providing again, they have to wait through the expiration time. + + +### checkinterval + +checkinterval is the seconds between updating the internal table. + +### trustProxy + +Defaults to true. If true then we use the x-forwarded-for header, otherwise we use the remote address. + +```js + var host = _params.trustProxy ? (req.headers['x-forwarded-for'] || req.connection.remoteAddress) : req.connection.remoteAddress +``` + +### includeUserAgent + +Defaults to true. If true we include the user agent as part of identifying a unique user. If false, then we only use IP. If set to false +this can lead to an entire block being banned unintentionally. Included to leave it up to the developer how they want to use it. + + +### whitelist + +Defaults to empty list. Specify the IP's or addresses you would like to whitelist + +```js + var Ddos = require('ddos'); + var ddos = new Ddos({whitelist:['74.125.224.72', '216.239.63.255']}); +``` + +Whitelisted IP's bypass all table checks. If the address in question is in IPV6 form, simply enable testmode + +```js + var ddos = new Ddos({whitelist:['74.125.224.72', '216.239.63.255'], testmode:true}); +``` + +and see the exact form of the address you want to whitelist. See this [link on stackoverflow about IPv6 addresses](http://stackoverflow.com/questions/29411551/express-js-req-ip-is-returning-ffff127-0-0-1) + +### .addWhitelist(ip) + +Update whitelist while running. + +```js + ddos.addWhitelist('74.125.224.72') +``` + +### errormessage + +When a request is denied, the user receives a 429 and the error message. + +### responseStatus + +By default HTTP status code 429 (Too Many Requests) are sent in response. + +### onDenial + +If this callback is specified, it will be called with the `req` object on a denial. Useful for logging. + +```js + const onDenial = function(req) { + // log it + } + const ddos = new Ddos({ limit: 2, onDenial }); +``` + +Contribute +========== + +Contributions welcome! + + +LICENSE +======= + +MIT diff --git a/node_modules/ddos/index.js b/node_modules/ddos/index.js new file mode 100644 index 0000000..36328ab --- /dev/null +++ b/node_modules/ddos/index.js @@ -0,0 +1,80 @@ +const lib = require("./lib"); +const defaultParams = require("./lib/defaults"); + +const ddos = function(params) { + if (!params) params = {}; + + params = Object.assign({}, defaultParams(), params); + params.maxcount = params.limit * 2; + + if (params.testmode) { + console.log("ddos: starting params: ", params); + } + + this.table = {}; + this.timer = setInterval(this.update.bind(this), params.checkinterval * 1000); + this.express = this.handle.bind(this); + this.middleware = this.handle.bind(this); + this.params = params; +}; + + +ddos.prototype.addWhitelist = lib.addWhitelist; +ddos.prototype.stop = lib.stop; +ddos.prototype.end = ddos.prototype.stop; +ddos.prototype.update = lib.update; +ddos.prototype.handle = lib.handle; +ddos.prototype.express = lib.handle; +ddos.prototype.koa = function() { + return function(ctx, next) { + var req = ctx.req; + var res = ctx.res; + + return lib._handle(this.params,this.table, req) + .then(() => { + return next() + }) + }; +}; + +ddos.prototype.hapi17 = function (request, h) { + const req = request.raw.req; + const params = this.params; + const table = this.table; + + + return lib._handle(params, table, req) + .then(() => { + return h.continue + }) + .catch((e) => { + if (e.action === "respond") { + const response = h.response(e.message); + response.takeover(); + response.code(e.code); + return response; + } + }) + + +} +ddos.prototype.hapi = function(request, reply) { + const req = request.raw.req; + const res = reply; + const table = this.table; + const params = this.params; + + return lib._handle(params, table, req) + .then(() => { + return reply.continue(); + }) + .catch((e) => { + if (e.action === "respond") { + return res(e.message).code(e.code); + } + }) + +}; +ddos.prototype.ipv4re = lib.ipv4re; + +module.exports = exports = ddos; diff --git a/node_modules/ddos/lib/defaults.js b/node_modules/ddos/lib/defaults.js new file mode 100644 index 0000000..e20de5d --- /dev/null +++ b/node_modules/ddos/lib/defaults.js @@ -0,0 +1,22 @@ + +function genDefaults () { + const defaults = {}; + // burst, maxexpiry, checkinterval is in seconds + defaults.maxcount = 30; + defaults.burst = 5; + defaults.checkinterval = 1; + + // limit is the maximum count + defaults.limit = defaults.burst * 4; + + defaults.maxexpiry = 120; + defaults.trustProxy = true; + defaults.includeUserAgent = true; + defaults.whitelist = []; + defaults.errormessage = "Error"; + defaults.testmode = false; + defaults.responseStatus = 429; + return defaults; +} + +module.exports = exports = genDefaults; diff --git a/node_modules/ddos/lib/index.js b/node_modules/ddos/lib/index.js new file mode 100644 index 0000000..2252783 --- /dev/null +++ b/node_modules/ddos/lib/index.js @@ -0,0 +1,112 @@ +const addWhitelist = function(ip) { + return this.params.whitelist.push(ip); +} +exports.addWhitelist = addWhitelist; + +const update = function() { + var keys = Object.keys(this.table); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + this.table[key].expiry -= this.params.checkinterval; + if (this.table[key].expiry <= 0) delete this.table[key]; + } + if (this.params.testmode) { + console.log(this.table); + } +}; +exports.update = update; + +const stop = function() { + clearInterval(this.timer); + this.params.stop = true; +}; + +exports.stop = stop; + +const ipv4re = new RegExp(/(::ffff:)?(\d+\.\d+.\d+\.\d+(:\d+)?)/); +exports.ipv4re = ipv4re; + + +const getAddress = (options, req) => { + let address = options.trustProxy + ? req.headers["x-forwarded-for"] || req.connection.remoteAddress + : req.connection.remoteAddress; + if (address === "::1") { + address="127.0.0.1" + } else { + let result = address.match(ipv4re) + if (result && result[2]) + address = result[2]; + else + address = "127.0.0.1"; + } + return address; +} + +const _handle = function(options, table, req) { + return new Promise((resolve, reject) => { + if (options.stop) { + return reject({action:"nothing", message:"stopped"}) + } + if (options.testmode) { + console.log("ddos: handle: beginning:", table); + } + let host = getAddress(options, req); + if (options.testmode) { + console.log("host:", host); + } + if (options.whitelist.indexOf(host) != -1) { + return resolve(); + } + if (options.includeUserAgent) + host = host.concat("#" + req.headers["user-agent"]); + if (!table[host]) + table[host] = { count: 1, expiry: 1 }; + else { + table[host].count++; + if (table[host].count > options.maxcount) + table[host].count = options.maxcount; + if ((table[host].count > options.burst) && (table[host].expiry <= options.maxexpiry)) { + table[host].expiry = Math.min( + options.maxexpiry, + table[host].expiry * 2 + ); + } else { + table[host].expiry = 1; + } + } + if (options.testmode) { + console.log("ddos: handle: end:", table); + } + + if (table[host].count > options.limit) { + (!options.testmode) && (console.log("ddos: denied: entry:", host, table[host])); + if (options.testmode) { + return reject({action:'respond', code:429, message:JSON.stringify(table[host])}); + } else { + return reject({action:'respond', code:options.responseStatus, message:options.errormessage}) + } + } else { + return resolve(); + } + }) +}; +exports._handle = _handle; + +exports.handle = function (req, res, next) { + return _handle(this.params, this.table, req) + .then(() => next()) + .catch((e) => { + if (e.action === "nothing") { + return next(); + } + if (e.action === "respond") { + if (this.params.onDenial) { + this.params.onDenial(req) + } + + res.writeHead(e.code, {'Content-Type':'application/json'}); + return res.end(e.message); + } + }) +}; diff --git a/node_modules/ddos/lib/koa.js b/node_modules/ddos/lib/koa.js new file mode 100644 index 0000000..a41f54d --- /dev/null +++ b/node_modules/ddos/lib/koa.js @@ -0,0 +1,10 @@ +const koa_handler = function(params, table, handle) { + return function(ctx, next) { + var req = ctx.req; + var res = ctx.res; + + return handle(params, table, req); + }; +}; + +module.exports = exports = koa_handler; diff --git a/node_modules/ddos/package.json b/node_modules/ddos/package.json new file mode 100644 index 0000000..67fd2cc --- /dev/null +++ b/node_modules/ddos/package.json @@ -0,0 +1,54 @@ +{ + "name": "ddos", + "version": "0.2.1", + "description": "", + "main": "index.js", + "scripts": { + "setup-helper": "node setupHelper.js", + "coverage": "nyc report --reporter=text-lcov | coveralls", + "test": "nyc tape test/test-*.js | tap-spec" + }, + "nyc": { + "exclude": [ + "lib/koa.js", + "test" + ] + }, + "repository": { + "type": "git", + "url": "git://github.com/rook2pawn/node-ddos.git" + }, + "keywords": [ + "ddos", + "rate", + "limiting", + "koa", + "express", + "hapijs", + "hapi" + ], + "author": "David Wee (http://rook2pawn.com)", + "license": "MIT", + "bugs": { + "url": "https://github.com/rook2pawn/node-ddos/issues" + }, + "homepage": "https://github.com/rook2pawn/node-ddos", + "devDependencies": { + "body-parser": "", + "coveralls": "^3.0.1", + "express": "", + "istanbul": "^0.4.5", + "koa": "", + "koa-router": "^7.4.0", + "npm-install-version": "^6.0.2", + "nyc": "^12.0.1", + "opener": "^1.5.1", + "queuelib": "", + "request": "", + "semver": "^5.3.0", + "supertest-light": "", + "tap-spec": "^4.1.2", + "tape": "" + }, + "dependencies": {} +} diff --git a/node_modules/ddos/setupHelper.js b/node_modules/ddos/setupHelper.js new file mode 100644 index 0000000..8c88e53 --- /dev/null +++ b/node_modules/ddos/setupHelper.js @@ -0,0 +1,17 @@ +var Ddos = require('./') +const opener = require("opener"); +var express = require('express') +var ddos = new Ddos({ + burst:4, + limit:4, + testmode:true +}); +var app = express(); +app.use(ddos.express); +app.get("/", (req,res,next) => { + console.log("Beep"); + res.end("Boop"); +}) +app.listen(5150, () => { + opener("http://127.0.0.1:5150"); +}); diff --git a/node_modules/ddos/stopcat.jpg b/node_modules/ddos/stopcat.jpg new file mode 100644 index 0000000..ef98d2e Binary files /dev/null and b/node_modules/ddos/stopcat.jpg differ diff --git a/node_modules/ddos/test/test-express.js b/node_modules/ddos/test/test-express.js new file mode 100644 index 0000000..09fe662 --- /dev/null +++ b/node_modules/ddos/test/test-express.js @@ -0,0 +1,112 @@ +var tape = require("tape"); +var express = require("express"); +var request = require("supertest-light"); +var QL = require("queuelib"); + +var Ddos = require("../"); + +tape("count and expiry test", function(t) { + t.plan(14); + + var ddos = new Ddos({ burst: 3, limit: 4 }); + var app = express(); + app.use(ddos.express); + var a = function(req, res, next) { + next(); + }; + var b = function(req, res, next) { + // some more random middleware + next(); + }; + var c = function(req, res, next) { + res.writeHead(200, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ foo: "bar" })); + }; + app.get("/article", a, b, c); + + var q = new QL(); + q.series( + [ + lib => { + request(app) + .get("/article") + .then(res => { + t.equal(res.statusCode, 200); + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 1, expiry: 1 }); + lib.done(); + }); + }, + lib => { + request(app) + .get("/article") + .then(res => { + t.equal(res.statusCode, 200); + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 2, expiry: 1 }); + lib.done(); + }); + }, + lib => { + request(app) + .get("/article") + .then(res => { + t.equal(res.statusCode, 200); + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 3, expiry: 1 }); + lib.done(); + }); + }, + lib => { + request(app) + .get("/article") + .then(res => { + t.equal(res.statusCode, 200); + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 4, expiry: 2 }); + lib.done(); + }); + }, + lib => { + request(app) + .get("/article") + .then(res => { + t.equal(res.statusCode, 429); + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 5, expiry: 4 }); + lib.done(); + }); + }, + lib => { + setTimeout(() => { + request(app) + .get("/article") + .then(res => { + t.equal(res.statusCode, 429); + var key = Object.keys(ddos.table)[0]; + // should start at {count:5, expiry:1} since 4 - 3 = 1 + // after a request, it should penalize for being over burst, which means + // expiry goes to 2 + t.deepEqual(ddos.table[key], { count: 6, expiry: 2 }); + lib.done(); + }); + }, 3100); + }, + lib => { + setTimeout(() => { + request(app) + .get("/article") + .then(res => { + t.equal(res.statusCode, 200); + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 1, expiry: 1 }); + lib.done(); + }); + }, 2100); + } + ], + function() { + ddos.end(); + } + ); +}); diff --git a/node_modules/ddos/test/test-hapi.js b/node_modules/ddos/test/test-hapi.js new file mode 100644 index 0000000..f2b7fae --- /dev/null +++ b/node_modules/ddos/test/test-hapi.js @@ -0,0 +1,77 @@ +var tape = require("tape"); + +tape("count and expiry test", function(t) { + const niv = require("npm-install-version"); + niv.install("hapi@16"); + var Hapi = require("hapi@16"); + var request = require("request"); + var QL = require("queuelib"); + + var Ddos = require("../"); + + t.plan(11); + var q = new QL(); + var ddos = new Ddos({ burst: 3, limit: 4 }); + + const server = new Hapi.Server(); + server.connection({ port: 3000, host: "localhost" }); + server.route({ + method: "GET", + path: "/", + handler: function(request, reply) { + reply("Hello, world!"); + } + }); + server.ext("onRequest", ddos.hapi.bind(ddos)); + server.start(err => { + q.series( + [ + lib => { + request("http://localhost:3000/", (err, res, body) => { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 1, expiry: 1 }); + t.equal(res.statusCode, 200); + lib.done(); + }); + }, + lib => { + request("http://localhost:3000/", (err, res, body) => { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 2, expiry: 1 }); + t.equal(res.statusCode, 200); + lib.done(); + }); + }, + lib => { + request("http://localhost:3000/", (err, res, body) => { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 3, expiry: 1 }); + t.equal(res.statusCode, 200); + lib.done(); + }); + }, + lib => { + request("http://localhost:3000/", (err, res, body) => { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 4, expiry: 2 }); + t.equal(res.statusCode, 200); + lib.done(); + }); + }, + lib => { + request("http://localhost:3000/", (err, res, body) => { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 5, expiry: 4 }); + t.equal(res.statusCode, 429); + lib.done(); + }); + } + ], + function() { + t.pass("ok"); + ddos.end(); + server.stop(); + } + ); + }); +}); diff --git a/node_modules/ddos/test/test-hapi17.js b/node_modules/ddos/test/test-hapi17.js new file mode 100644 index 0000000..8699a8e --- /dev/null +++ b/node_modules/ddos/test/test-hapi17.js @@ -0,0 +1,91 @@ +var tape = require("tape"); + + +tape("count and expiry test", function(t) { + var request = require("request"); + var QL = require("queuelib"); + + var Ddos = require("../"); + const niv = require("npm-install-version"); + niv.install("hapi@17"); + var Hapi = require("hapi@17"); + + + t.plan(11); + var q = new QL(); + var ddos = new Ddos({ burst: 3, limit: 4 }); + + const server = Hapi.server({ + port: 3000, + host: "localhost" + }); + + server.route({ + method: "GET", + path: "/", + handler: (request, h) => { + return "Hello, world!"; + } + }); + + server.route({ + method: "GET", + path: "/{name}", + handler: (request, h) => { + return "Hello, " + encodeURIComponent(request.params.name) + "!"; + } + }); + server.ext("onRequest", ddos.hapi17.bind(ddos)); + + server.start().then(() => { + q.series( + [ + lib => { + request("http://localhost:3000/", (err, res, body) => { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 1, expiry: 1 }); + t.equal(res.statusCode, 200); + lib.done(); + }); + }, + lib => { + request("http://localhost:3000/", (err, res, body) => { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 2, expiry: 1 }); + t.equal(res.statusCode, 200); + lib.done(); + }); + }, + lib => { + request("http://localhost:3000/", (err, res, body) => { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 3, expiry: 1 }); + t.equal(res.statusCode, 200); + lib.done(); + }); + }, + lib => { + request("http://localhost:3000/", (err, res, body) => { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 4, expiry: 2 }); + t.equal(res.statusCode, 200); + lib.done(); + }); + }, + lib => { + request("http://localhost:3000/", (err, res, body) => { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 5, expiry: 4 }); + t.equal(res.statusCode, 429); + lib.done(); + }); + } + ], + function() { + t.pass("ok"); + ddos.end(); + server.stop(); + } + ); + }); +}); diff --git a/node_modules/ddos/test/test-koa.js b/node_modules/ddos/test/test-koa.js new file mode 100644 index 0000000..60c29e7 --- /dev/null +++ b/node_modules/ddos/test/test-koa.js @@ -0,0 +1,49 @@ +var semver = require("semver"); + +if (semver.gte(process.version, "7.6.0")) { + var tape = require("tape"); + var koa = require("koa"); + var request = require("supertest-light"); + var DDOS = require("../"); + var ddos = new DDOS({ burst: 3, limit: 4, testmode: true }); + + const Router = require("koa-router"); + const router = new Router(); + var app = new koa(); + + router.get("/todos", ctx => { + ctx.status = 200; + ctx.body = [ + { + id: 1, + text: "Switch to Koa", + completed: true + }, + { + id: 2, + text: "???", + completed: true + }, + { + id: 3, + text: "Profit", + completed: true + } + ]; + }); + app.use(ddos.koa().bind(ddos)); + app.use(router.routes()); + + tape("table test", function(t) { + t.plan(3); + request(app.callback()) + .get("/todos") + .then(res => { + t.equal(res.statusCode, 200); + t.equal(res.headers["content-length"], "131"); + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 1, expiry: 1 }); + ddos.stop(); + }); + }); +} diff --git a/node_modules/ddos/test/test-localhost.js b/node_modules/ddos/test/test-localhost.js new file mode 100644 index 0000000..144ca18 --- /dev/null +++ b/node_modules/ddos/test/test-localhost.js @@ -0,0 +1,38 @@ +const tape = require("tape"); +const Ddos = require("../"); +const request = require("supertest-light"); +const express = require("express"); + +// https://github.com/rook2pawn/node-ddos/issues/31 + +tape("localhost ", function(t) { + t.plan(4); + + const ddos = new Ddos({ burst: 3, limit: 2 }); + t.equals("::ffff:127.0.0.1".match(ddos.ipv4re)[2], "127.0.0.1"); + t.equals("127.0.0.1".match(ddos.ipv4re)[2], "127.0.0.1"); + t.equals("32.45.32.65:12568".match(ddos.ipv4re)[2], "32.45.32.65:12568"); + + const app = express(); + app.use(ddos.express); + app.get("/user", (req, res) => { + res.status(200).json({ name: "john" }); + }); + + const doCall = function() { + return request(app) + .set("x-forwarded-for", "::1") + .get("/user"); + }; + + doCall() + .then(() => { + t.equals( + Object.keys(ddos.table)[0].match(/127\.0\.0\.1/)[0], + "127.0.0.1" + ); + }) + .then(() => { + ddos.end(); + }); +}); diff --git a/node_modules/ddos/test/test-maxcount.js b/node_modules/ddos/test/test-maxcount.js new file mode 100644 index 0000000..9934f64 --- /dev/null +++ b/node_modules/ddos/test/test-maxcount.js @@ -0,0 +1,36 @@ +const tape = require("tape"); +const Ddos = require("../"); +const request = require("supertest-light"); +const express = require("express"); + +tape("maxcount ", function(t) { + t.plan(1); + const ddos = new Ddos({ burst: 3, limit: 2 }); + const app = express(); + app.use(ddos.express); + app.get("/user", (req, res) => { + res.status(200).json({ name: "john" }); + }); + + const doCall = function() { + return request(app).get("/user"); + }; + + doCall() + .then(() => { + return doCall(); + }) + .then(() => { + return doCall(); + }) + .then(() => { + return doCall(); + }) + .then(() => { + return doCall(); + }) + .then(res => { + ddos.end(); + t.pass(); + }); +}); diff --git a/node_modules/ddos/test/test-options.js b/node_modules/ddos/test/test-options.js new file mode 100644 index 0000000..6a609ca --- /dev/null +++ b/node_modules/ddos/test/test-options.js @@ -0,0 +1,143 @@ +const tape = require("tape"); +const Ddos = require("../"); +const request = require("supertest-light"); +const express = require("express"); + +tape("options - whitelist ", function(t) { + t.plan(4); + const ddos = new Ddos({ limit: 1, whitelist: ["127.0.0.1"] }); + const app = express(); + let count = 0; + app.use(ddos.express); + app.get("/user", (req, res) => { + count++; + return res.status(200).json({ name: "john" }); + }); + + const doCall = function(code) { + return request(app) + .get("/user") + .then(res => { + t.equal(res.statusCode, code); + }) + .catch(e => { + t.fail(); + }); + }; + + doCall(200) + .then(() => doCall(200)) + .then(() => doCall(200)) + .then(() => { + t.equals(count, 3); + ddos.end(); + }); +}); + +tape("method - addwhitelist ", function(t) { + t.plan(5); + const ddos = new Ddos({ limit: 1 }); + const app = express(); + let count = 0; + app.use(ddos.express); + app.get("/user", (req, res) => { + count++; + return res.status(200).json({ name: "john" }); + }); + + const doCall = function(code) { + return request(app) + .get("/user") + .then(res => { + t.equal(res.statusCode, code); + }) + .catch(e => { + t.fail(); + }); + }; + + doCall(200) + .then(() => doCall(429)) + .then(() => { + t.equals(count, 1); + }) + .then(() => { + ddos.addWhitelist("127.0.0.1"); + return doCall(200); + }) + .then(() => { + t.equals(count, 2); + ddos.end(); + }); +}); + +tape("options - includeUserAgent ", function(t) { + t.plan(4); + const ddos = new Ddos({ includeUserAgent: false, burst: 3, limit: 4 }); + const app = express(); + app.use(ddos.express); + app.get("/user", (req, res) => { + res.status(200).json({ name: "john" }); + }); + + request(app) + .get("/user") + .then(res => { + t.equals(res.headers["content-type"], "application/json; charset=utf-8"); + t.equals(res.headers["content-length"], "15"); + t.equals(res.statusCode, 200); + }) + .then(() => { + ddos.end(); + t.pass(); + }); +}); + +tape("options - trustProxy ", function(t) { + t.plan(4); + const ddos = new Ddos({ trustProxy: false, burst: 3, limit: 4 }); + const app = express(); + app.use(ddos.express); + app.get("/user", (req, res) => { + res.status(200).json({ name: "john" }); + }); + + request(app) + .get("/user") + .then(res => { + t.equals(res.headers["content-type"], "application/json; charset=utf-8"); + t.equals(res.headers["content-length"], "15"); + t.equals(res.statusCode, 200); + }) + .then(() => { + ddos.end(); + t.pass(); + }); +}); + +tape("options - onDenial ", function(t) { + t.plan(1); + let count = 0; + const onDenial = function(req) { + count++; + }; + const ddos = new Ddos({ limit: 1, onDenial }); + const app = express(); + app.use(ddos.express); + app.get("/user", (req, res) => { + console.log("reply"); + res.status(200).json({ name: "john" }); + }); + + const doCall = function() { + return request(app).get("/user"); + }; + + doCall() + .then(() => doCall()) + .then(() => doCall()) + .then(res => { + t.equals(count, 2); + ddos.end(); + }); +}); diff --git a/node_modules/ddos/test/test-post.js b/node_modules/ddos/test/test-post.js new file mode 100644 index 0000000..cc0847c --- /dev/null +++ b/node_modules/ddos/test/test-post.js @@ -0,0 +1,97 @@ +var http = require("http"); +var express = require("express"); +var bodyParser = require("body-parser"); +var request = require("request"); +var Ddos = require("../"); +var ddos = new Ddos({ burst: 3, limit: 4, testmode: true }); +var app = express(); +app.use(ddos.express); +app.use(bodyParser.json()); +var server = http.createServer(app); + +var QL = require("queuelib"); +var q = new QL(); + +server.listen(5050); + +var a = function(req, res, next) { + next(); +}; +var b = function(req, res, next) { + // some more random middleware + next(); +}; +var c = function(req, res, next) { + var num = req.body.num * 2; + res.end(JSON.stringify({ foo: num })); +}; +app.post("/article", a, b, c); + +var tape = require("tape"); +tape("post test", function(t) { + t.plan(11); + q.series([ + function(lib) { + request.post( + { url: "http://localhost:5050/article", json: true, body: { num: 42 } }, + function(err, resp, body) { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 1, expiry: 1 }); + t.deepEqual(body, { foo: 84 }); + lib.done(); + } + ); + }, + function(lib) { + request.post( + { url: "http://localhost:5050/article", json: true, body: { num: 42 } }, + function(err, resp, body) { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 2, expiry: 1 }); + t.deepEqual(body, { foo: 84 }); + lib.done(); + } + ); + }, + function(lib) { + request.post( + { url: "http://localhost:5050/article", json: true, body: { num: 42 } }, + function(err, resp, body) { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 3, expiry: 1 }); + t.deepEqual(body, { foo: 84 }); + lib.done(); + } + ); + }, + function(lib) { + request.post( + { url: "http://localhost:5050/article", json: true, body: { num: 42 } }, + function(err, resp, body) { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 4, expiry: 2 }); + t.deepEqual(body, { foo: 84 }); + lib.done(); + } + ); + }, + function(lib) { + request.post( + { url: "http://localhost:5050/article", json: true, body: { num: 42 } }, + function(err, resp, body) { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 5, expiry: 4 }); + t.equal(resp.statusCode, 429, "should be 429"); + t.equal(body.count, 5, "should be 5"); + lib.done(); + } + ); + }, + function(lib) { + lib.done(); + t.end(); + server.close(); + ddos.stop(); + } + ]); +}); diff --git a/node_modules/ddos/test/test-stop.js b/node_modules/ddos/test/test-stop.js new file mode 100644 index 0000000..8602856 --- /dev/null +++ b/node_modules/ddos/test/test-stop.js @@ -0,0 +1,74 @@ +var http = require("http"); +var express = require("express"); +var bodyParser = require("body-parser"); +var request = require("request"); +var QL = require("queuelib"); +var Ddos = require("../"); +var ddos = new Ddos(); +var tape = require("tape"); + +tape("stop test", function(t) { + var app = express(); + app.use(ddos.express); + app.use(bodyParser.json()); + var q = new QL(); + var server = http.createServer(app); + server.listen(5050); + + var a = function(req, res, next) { + next(); + }; + var b = function(req, res, next) { + // some more random middleware + next(); + }; + var c = function(req, res, next) { + var num = req.body.num * 2; + res.end(JSON.stringify({ foo: num })); + }; + app.post("/article", a, b, c); + + t.plan(6); + q.series([ + function(lib) { + request.post( + { url: "http://localhost:5050/article", json: true, body: { num: 42 } }, + function(err, resp, body) { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 1, expiry: 1 }); + t.deepEqual(body, { foo: 84 }); + lib.done(); + } + ); + }, + function(lib) { + request.post( + { url: "http://localhost:5050/article", json: true, body: { num: 42 } }, + function(err, resp, body) { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 2, expiry: 1 }); + t.deepEqual(body, { foo: 84 }); + ddos.stop(); + lib.done(); + } + ); + }, + function(lib) { + request.post( + { url: "http://localhost:5050/article", json: true, body: { num: 42 } }, + function(err, resp, body) { + var key = Object.keys(ddos.table)[0]; + t.deepEqual(ddos.table[key], { count: 2, expiry: 1 }); + t.deepEqual(body, { foo: 84 }); + lib.done(); + } + ); + }, + function(lib) { + lib.done(); + t.end(); + server.close(); + ddos.stop(); + } + ]); +}); diff --git a/node_modules/dotenv/CHANGELOG.md b/node_modules/dotenv/CHANGELOG.md index c15fd30..4ad52e0 100644 --- a/node_modules/dotenv/CHANGELOG.md +++ b/node_modules/dotenv/CHANGELOG.md @@ -2,7 +2,27 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -## [Unreleased](https://github.com/motdotla/dotenv/compare/v16.3.1...master) +## [Unreleased](https://github.com/motdotla/dotenv/compare/v16.4.1...master) + +## [16.4.1](https://github.com/motdotla/dotenv/compare/v16.4.0...v16.4.1) (2024-01-24) + +- Patch support for array as `path` option [#797](https://github.com/motdotla/dotenv/pull/797) + +## [16.4.0](https://github.com/motdotla/dotenv/compare/v16.3.2...v16.4.0) (2024-01-23) + +- Add `error.code` to error messages around `.env.vault` decryption handling [#795](https://github.com/motdotla/dotenv/pull/795) +- Add ability to find `.env.vault` file when filename(s) passed as an array [#784](https://github.com/motdotla/dotenv/pull/784) + +## [16.3.2](https://github.com/motdotla/dotenv/compare/v16.3.1...v16.3.2) (2024-01-18) + +### Added + +- Add debug message when no encoding set [#735](https://github.com/motdotla/dotenv/pull/735) + +### Changed + +- Fix output typing for `populate` [#792](https://github.com/motdotla/dotenv/pull/792) +- Use subarray instead of slice [#793](https://github.com/motdotla/dotenv/pull/793) ## [16.3.1](https://github.com/motdotla/dotenv/compare/v16.3.0...v16.3.1) (2023-06-17) diff --git a/node_modules/dotenv/README-es.md b/node_modules/dotenv/README-es.md index ad8be92..fdad515 100644 --- a/node_modules/dotenv/README-es.md +++ b/node_modules/dotenv/README-es.md @@ -1,3 +1,9 @@ +
+🎉 announcing dotenvx. *run anywhere, multi-environment, encrypted envs*. +
+ +--- +

diff --git a/node_modules/dotenv/README.md b/node_modules/dotenv/README.md index a419157..63e8999 100644 --- a/node_modules/dotenv/README.md +++ b/node_modules/dotenv/README.md @@ -1,3 +1,9 @@ +

+🎉 announcing dotenvx. run anywhere, multi-environment, encrypted envs. +
+ +--- +

@@ -18,23 +24,23 @@


- +
- Retool + WorkOS
- Retool helps developers build custom internal software, like CRUD apps and admin panels, really fast. + Your App, Enterprise Ready.
- Build UIs visually with flexible components, connect to any data source, and write business logic in JavaScript. + Add Single Sign-On, Multi-Factor Auth, and more, in minutes instead of months.
-
- +
+
- WorkOS + Alloy Automation
- Your App, Enterprise Ready. + Launch user-facing integrations faster
- Add Single Sign-On, Multi-Factor Auth, and more, in minutes instead of months. + Easily spin up hundreds of integrations. Sign up free or read our docs first

@@ -44,7 +50,7 @@ dotenv -Dotenv is a zero-dependency module that loads environment variables from a `.env` file into [`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env). Storing configuration in the environment separate from code is based on [The Twelve-Factor App](http://12factor.net/config) methodology. +Dotenv is a zero-dependency module that loads environment variables from a `.env` file into [`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env). Storing configuration in the environment separate from code is based on [The Twelve-Factor App](https://12factor.net/config) methodology. [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard) [![LICENSE](https://img.shields.io/github/license/motdotla/dotenv.svg)](LICENSE) @@ -52,8 +58,8 @@ Dotenv is a zero-dependency module that loads environment variables from a `.env * [🌱 Install](#-install) * [🏗️ Usage (.env)](#%EF%B8%8F-usage) -* [🚀 Deploying (.env.vault) 🆕](#-deploying) * [🌴 Multiple Environments 🆕](#-manage-multiple-environments) +* [🚀 Deploying (.env.vault) 🆕](#-deploying) * [📚 Examples](#-examples) * [📖 Docs](#-documentation) * [❓ FAQ](#-faq) @@ -180,44 +186,45 @@ You need to add the value of another variable in one of your variables? Use [dot You need to keep `.env` files in sync between machines, environments, or team members? Use [dotenv-vault](https://github.com/dotenv-org/dotenv-vault). -### Deploying - -You need to deploy your secrets in a cloud-agnostic manner? Use a `.env.vault` file. - ### Multiple Environments You need to manage your secrets across different environments and apply them as needed? Use a `.env.vault` file with a `DOTENV_KEY`. -## 🚀 Deploying +### Deploying -*Note: Requires dotenv >= 16.1.0* +You need to deploy your secrets in a cloud-agnostic manner? Use a `.env.vault` file. See [deploying `.env.vault` files](https://github.com/motdotla/dotenv/tree/master#-deploying). -Encrypt your `.env.vault` file. +## 🌴 Manage Multiple Environments -```bash -$ npx dotenv-vault build -``` +Use [dotenvx](https://github.com/dotenvx/dotenvx) or [dotenv-vault](https://github.com/dotenv-org/dotenv-vault). -Fetch your production `DOTENV_KEY`. +### dotenvx + +Run any environment locally. Create a `.env.ENVIRONMENT` file and use `--env-file` to load it. It's straightforward, yet flexible. ```bash -$ npx dotenv-vault keys production +$ echo "HELLO=production" > .env.production +$ echo "console.log('Hello ' + process.env.HELLO)" > index.js + +$ dotenvx run --env-file=.env.production -- node index.js +Hello production +> ^^ ``` -Set `DOTENV_KEY` on your server. +or with multiple .env files ```bash -# heroku example -heroku config:set DOTENV_KEY=dotenv://:key_1234…@dotenv.org/vault/.env.vault?environment=production -``` +$ echo "HELLO=local" > .env.local +$ echo "HELLO=World" > .env +$ echo "console.log('Hello ' + process.env.HELLO)" > index.js -That's it! On deploy, your `.env.vault` file will be decrypted and its secrets injected as environment variables – just in time. - -*ℹ️ A note from [Mot](https://github.com/motdotla): Until recently, we did not have an opinion on how and where to store your secrets in production. We now strongly recommend generating a `.env.vault` file. It's the best way to prevent your secrets from being scattered across multiple servers and cloud providers – protecting you from breaches like the [CircleCI breach](https://techcrunch.com/2023/01/05/circleci-breach/). Also it unlocks interoperability WITHOUT native third-party integrations. Third-party integrations are [increasingly risky](https://coderpad.io/blog/development/heroku-github-breach/) to our industry. They may be the 'du jour' of today, but we imagine a better future.* +$ dotenvx run --env-file=.env.local --env-file=.env -- node index.js +Hello local +``` -Learn more at dotenv-vault: Deploying +[more environment examples](https://dotenvx.com/docs/quickstart/environments) -## 🌴 Manage Multiple Environments +### dotenv-vault Edit your production environment variables. @@ -235,27 +242,56 @@ $ npx dotenv-vault build Learn more at dotenv-vault: Manage Multiple Environments +## 🚀 Deploying + +*Note: Requires dotenv >= 16.1.0* + +Encrypt your `.env.vault` file. + +```bash +$ npx dotenv-vault build +``` + +Fetch your production `DOTENV_KEY`. + +```bash +$ npx dotenv-vault keys production +``` + +Set `DOTENV_KEY` on your server. + +```bash +# heroku example +heroku config:set DOTENV_KEY=dotenv://:key_1234…@dotenv.org/vault/.env.vault?environment=production +``` + +That's it! On deploy, your `.env.vault` file will be decrypted and its secrets injected as environment variables – just in time. + +*ℹ️ A note from [Mot](https://github.com/motdotla): Until recently, we did not have an opinion on how and where to store your secrets in production. We now strongly recommend generating a `.env.vault` file. It's the best way to prevent your secrets from being scattered across multiple servers and cloud providers – protecting you from breaches like the [CircleCI breach](https://techcrunch.com/2023/01/05/circleci-breach/). Also it unlocks interoperability WITHOUT native third-party integrations. Third-party integrations are [increasingly risky](https://coderpad.io/blog/development/heroku-github-breach/) to our industry. They may be the 'du jour' of today, but we imagine a better future.* + +Learn more at dotenv-vault: Deploying + ## 📚 Examples See [examples](https://github.com/dotenv-org/examples) of using dotenv with various frameworks, languages, and configurations. -* [nodejs](https://github.com/dotenv-org/examples/tree/master/dotenv-nodejs) -* [nodejs (debug on)](https://github.com/dotenv-org/examples/tree/master/dotenv-nodejs-debug) -* [nodejs (override on)](https://github.com/dotenv-org/examples/tree/master/dotenv-nodejs-override) -* [nodejs (processEnv override)](https://github.com/dotenv-org/examples/tree/master/dotenv-custom-target) -* [nodejs (DOTENV_KEY override)](https://github.com/dotenv-org/examples/tree/master/dotenv-vault-custom-target) -* [esm](https://github.com/dotenv-org/examples/tree/master/dotenv-esm) -* [esm (preload)](https://github.com/dotenv-org/examples/tree/master/dotenv-esm-preload) -* [typescript](https://github.com/dotenv-org/examples/tree/master/dotenv-typescript) -* [typescript parse](https://github.com/dotenv-org/examples/tree/master/dotenv-typescript-parse) -* [typescript config](https://github.com/dotenv-org/examples/tree/master/dotenv-typescript-config) -* [webpack](https://github.com/dotenv-org/examples/tree/master/dotenv-webpack) -* [webpack (plugin)](https://github.com/dotenv-org/examples/tree/master/dotenv-webpack2) -* [react](https://github.com/dotenv-org/examples/tree/master/dotenv-react) -* [react (typescript)](https://github.com/dotenv-org/examples/tree/master/dotenv-react-typescript) -* [express](https://github.com/dotenv-org/examples/tree/master/dotenv-express) -* [nestjs](https://github.com/dotenv-org/examples/tree/master/dotenv-nestjs) -* [fastify](https://github.com/dotenv-org/examples/tree/master/dotenv-fastify) +* [nodejs](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-nodejs) +* [nodejs (debug on)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-nodejs-debug) +* [nodejs (override on)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-nodejs-override) +* [nodejs (processEnv override)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-custom-target) +* [nodejs (DOTENV_KEY override)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-vault-custom-target) +* [esm](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-esm) +* [esm (preload)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-esm-preload) +* [typescript](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-typescript) +* [typescript parse](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-typescript-parse) +* [typescript config](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-typescript-config) +* [webpack](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-webpack) +* [webpack (plugin)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-webpack2) +* [react](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-react) +* [react (typescript)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-react-typescript) +* [express](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-express) +* [nestjs](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-nestjs) +* [fastify](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-fastify) ## 📖 Documentation @@ -296,6 +332,12 @@ Specify a custom path if your file containing environment variables is located e require('dotenv').config({ path: '/custom/path/to/.env' }) ``` +By default, `config` will look for a file called .env in the current working directory. Pass in multiple files as an array, and they will be loaded in order. The first value set for a variable will win. + +```js +require('dotenv').config({ path: ['.env.local', '.env'] }) +``` + ##### encoding Default: `utf8` @@ -459,7 +501,7 @@ password than your development database. ### Should I have multiple `.env` files? -No. We **strongly** recommend against having a "main" `.env` file and an "environment" `.env` file like `.env.test`. Your config should vary between deploys, and you should not be sharing values between environments. +We recommend creating on `.env` file per environment. Use `.env` for local/development, `.env.production` for production and so on. This still follows the twelve factor principles as each is attributed individually to its own environment. Avoid custom set ups that work in inheritance somehow (`.env.production` inherits values form `.env` for example). It is better to duplicate values if necessary across each `.env.environment` file. > In a twelve-factor app, env vars are granular controls, each fully orthogonal to other env vars. They are never grouped together as “environments”, but instead are independently managed for each deploy. This is a model that scales up smoothly as the app naturally expands into more deploys over its lifetime. > @@ -561,7 +603,7 @@ import errorReporter from './errorReporter.mjs' errorReporter.report(new Error('documented example')) ``` -Does that make sense? It's a bit unintuitive, but it is how importing of ES6 modules work. Here is a [working example of this pitfall](https://github.com/dotenv-org/examples/tree/master/dotenv-es6-import-pitfall). +Does that make sense? It's a bit unintuitive, but it is how importing of ES6 modules work. Here is a [working example of this pitfall](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-es6-import-pitfall). There are two alternatives to this approach: @@ -618,6 +660,28 @@ Use [dotenv-vault](https://github.com/dotenv-org/dotenv-vault) A `.env.vault` file is an encrypted version of your development (and ci, staging, production, etc) environment variables. It is paired with a `DOTENV_KEY` to deploy your secrets more securely than scattering them across multiple platforms and tools. Use [dotenv-vault](https://github.com/dotenv-org/dotenv-vault) to manage and generate them. +### What if I accidentally commit my `.env` file to code? + +Remove it, [remove git history](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository) and then install the [git pre-commit hook](https://github.com/dotenvx/dotenvx#pre-commit) to prevent this from ever happening again. + +``` +brew install dotenvx/brew/dotenvx +dotenvx precommit --install +``` + +### How can I prevent committing my `.env` file to a Docker build? + +Use the [docker prebuild hook](https://dotenvx.com/docs/features/prebuild). + +```bash +# Dockerfile +... +RUN curl -fsS https://dotenvx.sh/ | sh +... +RUN dotenvx prebuild +CMD ["dotenvx", "run", "--", "node", "index.js"] +``` + ## Contributing Guide See [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/node_modules/dotenv/lib/main.d.ts b/node_modules/dotenv/lib/main.d.ts index af84746..965988e 100644 --- a/node_modules/dotenv/lib/main.d.ts +++ b/node_modules/dotenv/lib/main.d.ts @@ -1,6 +1,6 @@ // TypeScript Version: 3.0 /// -import type { URL } from 'node:url'; +import type { URL } from 'url'; export interface DotenvParseOutput { [name: string]: string; @@ -12,7 +12,6 @@ export interface DotenvParseOutput { * See https://docs.dotenv.org * * @param src - contents to be parsed. example: `'DB_HOST=localhost'` - * @param options - additional options. example: `{ debug: true }` * @returns an object with keys and values based on `src`. example: `{ DB_HOST : 'localhost' }` */ export function parse( @@ -24,10 +23,12 @@ export interface DotenvConfigOptions { * Default: `path.resolve(process.cwd(), '.env')` * * Specify a custom path if your file containing environment variables is located elsewhere. + * Can also be an array of strings, specifying multiple paths. * * example: `require('dotenv').config({ path: '/custom/path/to/.env' })` + * example: `require('dotenv').config({ path: ['/path/to/first.env', '/path/to/second.env'] })` */ - path?: string | URL; + path?: string | string[] | URL; /** * Default: `utf8` @@ -100,10 +101,6 @@ export interface DotenvPopulateOptions { override?: boolean; } -export interface DotenvPopulateOutput { - error?: Error; -} - export interface DotenvPopulateInput { [name: string]: string; } @@ -141,7 +138,7 @@ export function configDotenv(options?: DotenvConfigOptions): DotenvConfigOutput; * @returns {void} * */ -export function populate(processEnv: DotenvPopulateInput, parsed: DotenvPopulateInput, options?: DotenvConfigOptions): DotenvPopulateOutput; +export function populate(processEnv: DotenvPopulateInput, parsed: DotenvPopulateInput, options?: DotenvConfigOptions): void; /** * Decrypt ciphertext diff --git a/node_modules/dotenv/lib/main.js b/node_modules/dotenv/lib/main.js index 84f81a1..069f49c 100644 --- a/node_modules/dotenv/lib/main.js +++ b/node_modules/dotenv/lib/main.js @@ -53,7 +53,9 @@ function _parseVault (options) { // Parse .env.vault const result = DotenvModule.configDotenv({ path: vaultPath }) if (!result.parsed) { - throw new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`) + const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`) + err.code = 'MISSING_DATA' + throw err } // handle scenario for comma separated keys - for use with key rotation @@ -121,7 +123,9 @@ function _instructions (result, dotenvKey) { uri = new URL(dotenvKey) } catch (error) { if (error.code === 'ERR_INVALID_URL') { - throw new Error('INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenv.org/vault/.env.vault?environment=development') + const err = new Error('INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenv.org/vault/.env.vault?environment=development') + err.code = 'INVALID_DOTENV_KEY' + throw err } throw error @@ -130,34 +134,53 @@ function _instructions (result, dotenvKey) { // Get decrypt key const key = uri.password if (!key) { - throw new Error('INVALID_DOTENV_KEY: Missing key part') + const err = new Error('INVALID_DOTENV_KEY: Missing key part') + err.code = 'INVALID_DOTENV_KEY' + throw err } // Get environment const environment = uri.searchParams.get('environment') if (!environment) { - throw new Error('INVALID_DOTENV_KEY: Missing environment part') + const err = new Error('INVALID_DOTENV_KEY: Missing environment part') + err.code = 'INVALID_DOTENV_KEY' + throw err } // Get ciphertext payload const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}` const ciphertext = result.parsed[environmentKey] // DOTENV_VAULT_PRODUCTION if (!ciphertext) { - throw new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`) + const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`) + err.code = 'NOT_FOUND_DOTENV_ENVIRONMENT' + throw err } return { ciphertext, key } } function _vaultPath (options) { - let dotenvPath = path.resolve(process.cwd(), '.env') + let possibleVaultPath = null if (options && options.path && options.path.length > 0) { - dotenvPath = options.path + if (Array.isArray(options.path)) { + for (const filepath of options.path) { + if (fs.existsSync(filepath)) { + possibleVaultPath = filepath.endsWith('.vault') ? filepath : `${filepath}.vault` + } + } + } else { + possibleVaultPath = options.path.endsWith('.vault') ? options.path : `${options.path}.vault` + } + } else { + possibleVaultPath = path.resolve(process.cwd(), '.env.vault') } - // Locate .env.vault - return dotenvPath.endsWith('.vault') ? dotenvPath : `${dotenvPath}.vault` + if (fs.existsSync(possibleVaultPath)) { + return possibleVaultPath + } + + return null } function _resolveHome (envPath) { @@ -186,10 +209,25 @@ function configDotenv (options) { if (options) { if (options.path != null) { - dotenvPath = _resolveHome(options.path) + let envPath = options.path + + if (Array.isArray(envPath)) { + for (const filepath of options.path) { + if (fs.existsSync(filepath)) { + envPath = filepath + break + } + } + } + + dotenvPath = _resolveHome(envPath) } if (options.encoding != null) { encoding = options.encoding + } else { + if (debug) { + _debug('No encoding is specified. UTF-8 is used by default') + } } } @@ -216,15 +254,15 @@ function configDotenv (options) { // Populates process.env from .env file function config (options) { - const vaultPath = _vaultPath(options) - // fallback to original dotenv if DOTENV_KEY is not set if (_dotenvKey(options).length === 0) { return DotenvModule.configDotenv(options) } + const vaultPath = _vaultPath(options) + // dotenvKey exists but .env.vault file does not exist - if (!fs.existsSync(vaultPath)) { + if (!vaultPath) { _warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`) return DotenvModule.configDotenv(options) @@ -237,9 +275,9 @@ function decrypt (encrypted, keyStr) { const key = Buffer.from(keyStr.slice(-64), 'hex') let ciphertext = Buffer.from(encrypted, 'base64') - const nonce = ciphertext.slice(0, 12) - const authTag = ciphertext.slice(-16) - ciphertext = ciphertext.slice(12, -16) + const nonce = ciphertext.subarray(0, 12) + const authTag = ciphertext.subarray(-16) + ciphertext = ciphertext.subarray(12, -16) try { const aesgcm = crypto.createDecipheriv('aes-256-gcm', key, nonce) @@ -251,14 +289,14 @@ function decrypt (encrypted, keyStr) { const decryptionFailed = error.message === 'Unsupported state or unable to authenticate data' if (isRange || invalidKeyLength) { - const msg = 'INVALID_DOTENV_KEY: It must be 64 characters long (or more)' - throw new Error(msg) + const err = new Error('INVALID_DOTENV_KEY: It must be 64 characters long (or more)') + err.code = 'INVALID_DOTENV_KEY' + throw err } else if (decryptionFailed) { - const msg = 'DECRYPTION_FAILED: Please check your DOTENV_KEY' - throw new Error(msg) + const err = new Error('DECRYPTION_FAILED: Please check your DOTENV_KEY') + err.code = 'DECRYPTION_FAILED' + throw err } else { - console.error('Error: ', error.code) - console.error('Error: ', error.message) throw error } } @@ -270,7 +308,9 @@ function populate (processEnv, parsed, options = {}) { const override = Boolean(options && options.override) if (typeof parsed !== 'object') { - throw new Error('OBJECT_REQUIRED: Please check the processEnv argument being passed to populate') + const err = new Error('OBJECT_REQUIRED: Please check the processEnv argument being passed to populate') + err.code = 'OBJECT_REQUIRED' + throw err } // Set process.env diff --git a/node_modules/dotenv/package.json b/node_modules/dotenv/package.json index 012d789..1483953 100644 --- a/node_modules/dotenv/package.json +++ b/node_modules/dotenv/package.json @@ -1,6 +1,6 @@ { "name": "dotenv", - "version": "16.3.1", + "version": "16.4.1", "description": "Loads environment variables from .env file", "main": "lib/main.js", "types": "lib/main.d.ts", diff --git a/node_modules/nodemon/README.md b/node_modules/nodemon/README.md index f16b41f..f942bf7 100644 --- a/node_modules/nodemon/README.md +++ b/node_modules/nodemon/README.md @@ -378,7 +378,6 @@ Support this project by becoming a sponsor. Your logo will show up here with a l freebets.ltd.uk null Marketing -Best Online Casino Guide in Australia Rating of best betting sites in Australia null null @@ -411,11 +410,9 @@ Support this project by becoming a sponsor. Your logo will show up here with a l Find the best casinos online. Casinot.biz lists and reviews online casinos. null Norway -null -Online Casinos Spelen +OnlineCasinosSpelen Beoordelen van nieuwe online casino CasinoZonderRegistratie.net - Nederlandse Top Casino -Onlinecasinoprofy is your guide to the world of gambling. OSLabs is a nonprofit tech accelerator devoted to furthering high-impact open source software within a collaborative community of dedicated engineers and mentors Ilmaiset Pitkävetovihjeet NyeCasino.me is a website that lists the newest and best online casinos in Norway @@ -433,12 +430,25 @@ Support this project by becoming a sponsor. Your logo will show up here with a l Online Casinos Australia Looking to boost your YouTube channel? Buy YouTube subscribers with Views4You and watch your audience grow! null -Check out highest payout online casinos in Canada at leafletcasino.com +Verified reviews of the most reputable Canadian online casinos at leafletcasino.com Services to make the world a better place Najděte nejlepší online casino v České republice We review the entire iGaming industry from A to Z Helping Swedes finding safe unlicensed casinos -free spins no deposit +free spins no deposit +I migliori casinò online dagli esperti di SitiCasinoNonAAMS +Find the best Australian online casino and get bonuses for a successful start +null +PopularityBazaar helps you quickly grow your social media accounts. Buy 100% real likes, followers, views, comments, and more to kickstart your online presence. +Non-GamStop NonStop Casino +philippinescasinos.ph +null +NonGamStopBets Casinos not on GamStop +Buy real Instagram followers from Stormlikes starting at only $2.97. Stormlikes has been voted the best site to buy followers from the likes of US Magazine. +UpGrow is the Best Instagram Growth Service in 2024. Get more real Instagram followers with our AI-powered growth engine to get 10x faster results. +Reviewing companies in high-risk industries like online casinos, forex brokers and crypto exchanges +guide to online casinos not on gamstop +Analysis of payment methods for use in the iGaming
Please note that links to the sponsors above are not direct endorsements nor affiliated with any of contributors of the nodemon project. diff --git a/node_modules/nodemon/lib/config/defaults.js b/node_modules/nodemon/lib/config/defaults.js index c1795b9..dc95d34 100644 --- a/node_modules/nodemon/lib/config/defaults.js +++ b/node_modules/nodemon/lib/config/defaults.js @@ -25,7 +25,9 @@ const defaults = { watchOptions: {}, }; -if ((process.env.NODE_OPTIONS || '').includes('--loader')) { +const nodeOptions = process.env.NODE_OPTIONS || ''; // ? + +if (/--(loader|import)\b/.test(nodeOptions)) { delete defaults.execMap.ts; } diff --git a/node_modules/nodemon/package.json b/node_modules/nodemon/package.json index 2ff62b7..f99ae46 100644 --- a/node_modules/nodemon/package.json +++ b/node_modules/nodemon/package.json @@ -66,7 +66,7 @@ "touch": "^3.1.0", "undefsafe": "^2.0.5" }, - "version": "3.0.2", + "version": "3.0.3", "funding": { "type": "opencollective", "url": "https://opencollective.com/nodemon" diff --git a/package-lock.json b/package-lock.json index 27175b1..c7e6ed1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "bootstrap": "^5.3.2", "bufferutil": "^4.0.7", "cors": "^2.8.5", + "ddos": "^0.2.1", "detective-cjs": "^5.0.1", "discord.js": "^14.14.1", "dotenv": "^16.3.1", @@ -474,6 +475,11 @@ "node": ">= 12" } }, + "node_modules/ddos": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ddos/-/ddos-0.2.1.tgz", + "integrity": "sha512-IDTS5NBK0PzbYWqXhZyIVkrKGdvC+lEfOUF8HWWDYdvH8pr4s0eQNDNgWmaSQhZWe8GXQcyqujdQXklML4d9LQ==" + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", diff --git a/package.json b/package.json index 5942130..4afbbfc 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "bootstrap": "^5.3.2", "bufferutil": "^4.0.7", "cors": "^2.8.5", + "ddos": "^0.2.1", "detective-cjs": "^5.0.1", "discord.js": "^14.14.1", "dotenv": "^16.3.1",