fix: only rate limit login when failed (#2355)

This commit is contained in:
Daniel Salazar
2026-01-27 18:24:05 -08:00
committed by GitHub
parent e7443338fb
commit cc5285f88a
3 changed files with 923 additions and 148 deletions
+775 -19
View File
@@ -1882,7 +1882,6 @@
},
"node_modules/@csstools/color-helpers": {
"version": "5.1.0",
"dev": true,
"funding": [
{
"type": "github",
@@ -1900,7 +1899,6 @@
},
"node_modules/@csstools/css-calc": {
"version": "2.1.4",
"dev": true,
"funding": [
{
"type": "github",
@@ -1922,7 +1920,6 @@
},
"node_modules/@csstools/css-color-parser": {
"version": "3.1.0",
"dev": true,
"funding": [
{
"type": "github",
@@ -1948,7 +1945,6 @@
},
"node_modules/@csstools/css-parser-algorithms": {
"version": "3.0.5",
"dev": true,
"funding": [
{
"type": "github",
@@ -1987,7 +1983,6 @@
},
"node_modules/@csstools/css-tokenizer": {
"version": "3.0.4",
"dev": true,
"funding": [
{
"type": "github",
@@ -5736,6 +5731,13 @@
"@types/node": "*"
}
},
"node_modules/@types/highlight.js": {
"version": "9.12.4",
"resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.4.tgz",
"integrity": "sha512-t2szdkwmg2JJyuCM20e8kR2X59WCE5Zkl4bzm1u1Oukjm79zpbiAv+QjnwLnuuV0WHEcX2NgUItu0pAMKuOPww==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/html-minifier-terser": {
"version": "6.1.0",
"dev": true,
@@ -5761,6 +5763,16 @@
"@types/node": "*"
}
},
"node_modules/@types/jquery": {
"version": "3.5.33",
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.33.tgz",
"integrity": "sha512-SeyVJXlCZpEki5F0ghuYe+L+PprQta6nRZqhONt9F13dWBtR/ftoaIbdRQ7cis7womE+X2LKhsDdDtkkDhJS6g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/sizzle": "*"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"license": "MIT"
@@ -5958,6 +5970,13 @@
"version": "1.2.0",
"license": "MIT"
},
"node_modules/@types/sizzle": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.10.tgz",
"integrity": "sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/tedious": {
"version": "4.0.14",
"license": "MIT",
@@ -8091,7 +8110,6 @@
},
"node_modules/decimal.js": {
"version": "10.6.0",
"dev": true,
"license": "MIT"
},
"node_modules/decode-bmp": {
@@ -8288,6 +8306,10 @@
"version": "2.1.0",
"license": "MIT"
},
"node_modules/docs": {
"resolved": "src/docs",
"link": true
},
"node_modules/doctrine": {
"version": "3.0.0",
"license": "Apache-2.0",
@@ -8623,7 +8645,6 @@
},
"node_modules/entities": {
"version": "6.0.1",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -9513,7 +9534,6 @@
},
"node_modules/fs-extra": {
"version": "11.3.2",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
@@ -10054,7 +10074,6 @@
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"dev": true,
"license": "ISC"
},
"node_modules/graphemer": {
@@ -10550,7 +10569,6 @@
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
@@ -10863,7 +10881,6 @@
},
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"dev": true,
"license": "MIT"
},
"node_modules/is-regexp": {
@@ -11337,7 +11354,6 @@
},
"node_modules/jsonfile": {
"version": "6.2.0",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
@@ -12159,6 +12175,18 @@
"semver": "bin/semver.js"
}
},
"node_modules/marked": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz",
"integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"license": "MIT",
@@ -12916,6 +12944,12 @@
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
"node_modules/nwsapi": {
"version": "2.2.23",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz",
"integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==",
"license": "MIT"
},
"node_modules/nyc": {
"version": "15.1.0",
"dev": true,
@@ -14372,6 +14406,12 @@
"fsevents": "~2.3.2"
}
},
"node_modules/rrweb-cssom": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
"integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
"license": "MIT"
},
"node_modules/run-applescript": {
"version": "7.1.0",
"license": "MIT",
@@ -14447,7 +14487,6 @@
},
"node_modules/saxes": {
"version": "6.0.0",
"dev": true,
"license": "ISC",
"dependencies": {
"xmlchars": "^2.2.0"
@@ -15433,7 +15472,6 @@
},
"node_modules/symbol-tree": {
"version": "3.2.4",
"dev": true,
"license": "MIT"
},
"node_modules/tapable": {
@@ -15972,7 +16010,6 @@
},
"node_modules/universalify": {
"version": "2.0.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
@@ -16247,7 +16284,6 @@
},
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
"dev": true,
"license": "MIT",
"dependencies": {
"xml-name-validator": "^5.0.0"
@@ -16467,7 +16503,6 @@
},
"node_modules/whatwg-mimetype": {
"version": "4.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
@@ -16691,7 +16726,6 @@
},
"node_modules/xml-name-validator": {
"version": "5.0.0",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18"
@@ -16721,7 +16755,6 @@
},
"node_modules/xmlchars": {
"version": "2.2.0",
"dev": true,
"license": "MIT"
},
"node_modules/xmlhttprequest-ssl": {
@@ -18705,6 +18738,729 @@
"module-details-from-path": "^1.0.3"
}
},
"src/docs": {
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"fs-extra": "^11.2.0",
"html-entities": "^2.3.3",
"js-yaml": "^4.1.0",
"jsdom": "^26.1.0",
"marked": "^11.1.1"
},
"devDependencies": {
"@types/highlight.js": "^9.12.4",
"@types/jquery": "^3.5.33",
"concurrently": "^8.2.2",
"esbuild": "0.25.11",
"http-server": "^14.1.1",
"nodemon": "^3.1.4"
}
},
"src/docs/node_modules/@asamuzakjp/css-color": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
"integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
"license": "MIT",
"dependencies": {
"@csstools/css-calc": "^2.1.3",
"@csstools/css-color-parser": "^3.0.9",
"@csstools/css-parser-algorithms": "^3.0.4",
"@csstools/css-tokenizer": "^3.0.3",
"lru-cache": "^10.4.3"
}
},
"src/docs/node_modules/@esbuild/aix-ppc64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz",
"integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/android-arm": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz",
"integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/android-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz",
"integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/android-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz",
"integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/darwin-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz",
"integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/darwin-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz",
"integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz",
"integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/freebsd-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz",
"integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/linux-arm": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz",
"integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/linux-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz",
"integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/linux-ia32": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz",
"integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/linux-loong64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz",
"integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/linux-mips64el": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz",
"integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/linux-ppc64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz",
"integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/linux-riscv64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz",
"integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/linux-s390x": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz",
"integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/linux-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz",
"integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz",
"integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/netbsd-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz",
"integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz",
"integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/openbsd-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz",
"integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz",
"integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/sunos-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz",
"integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/win32-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz",
"integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/win32-ia32": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz",
"integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/@esbuild/win32-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz",
"integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/agent-base": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"src/docs/node_modules/cssstyle": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
"integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
"license": "MIT",
"dependencies": {
"@asamuzakjp/css-color": "^3.2.0",
"rrweb-cssom": "^0.8.0"
},
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/data-urls": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
"integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
"license": "MIT",
"dependencies": {
"whatwg-mimetype": "^4.0.0",
"whatwg-url": "^14.0.0"
},
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/esbuild": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz",
"integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.11",
"@esbuild/android-arm": "0.25.11",
"@esbuild/android-arm64": "0.25.11",
"@esbuild/android-x64": "0.25.11",
"@esbuild/darwin-arm64": "0.25.11",
"@esbuild/darwin-x64": "0.25.11",
"@esbuild/freebsd-arm64": "0.25.11",
"@esbuild/freebsd-x64": "0.25.11",
"@esbuild/linux-arm": "0.25.11",
"@esbuild/linux-arm64": "0.25.11",
"@esbuild/linux-ia32": "0.25.11",
"@esbuild/linux-loong64": "0.25.11",
"@esbuild/linux-mips64el": "0.25.11",
"@esbuild/linux-ppc64": "0.25.11",
"@esbuild/linux-riscv64": "0.25.11",
"@esbuild/linux-s390x": "0.25.11",
"@esbuild/linux-x64": "0.25.11",
"@esbuild/netbsd-arm64": "0.25.11",
"@esbuild/netbsd-x64": "0.25.11",
"@esbuild/openbsd-arm64": "0.25.11",
"@esbuild/openbsd-x64": "0.25.11",
"@esbuild/openharmony-arm64": "0.25.11",
"@esbuild/sunos-x64": "0.25.11",
"@esbuild/win32-arm64": "0.25.11",
"@esbuild/win32-ia32": "0.25.11",
"@esbuild/win32-x64": "0.25.11"
}
},
"src/docs/node_modules/html-encoding-sniffer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
"integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
"license": "MIT",
"dependencies": {
"whatwg-encoding": "^3.1.1"
},
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/http-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.0",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"src/docs/node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"src/docs/node_modules/jsdom": {
"version": "26.1.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
"integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
"license": "MIT",
"dependencies": {
"cssstyle": "^4.2.1",
"data-urls": "^5.0.0",
"decimal.js": "^10.5.0",
"html-encoding-sniffer": "^4.0.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"is-potential-custom-element-name": "^1.0.1",
"nwsapi": "^2.2.16",
"parse5": "^7.2.1",
"rrweb-cssom": "^0.8.0",
"saxes": "^6.0.0",
"symbol-tree": "^3.2.4",
"tough-cookie": "^5.1.1",
"w3c-xmlserializer": "^5.0.0",
"webidl-conversions": "^7.0.0",
"whatwg-encoding": "^3.1.1",
"whatwg-mimetype": "^4.0.0",
"whatwg-url": "^14.1.1",
"ws": "^8.18.0",
"xml-name-validator": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"canvas": "^3.0.0"
},
"peerDependenciesMeta": {
"canvas": {
"optional": true
}
}
},
"src/docs/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC"
},
"src/docs/node_modules/parse5": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
"license": "MIT",
"dependencies": {
"entities": "^6.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"src/docs/node_modules/tldts": {
"version": "6.1.86",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
"integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
"license": "MIT",
"dependencies": {
"tldts-core": "^6.1.86"
},
"bin": {
"tldts": "bin/cli.js"
}
},
"src/docs/node_modules/tldts-core": {
"version": "6.1.86",
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
"integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
"license": "MIT"
},
"src/docs/node_modules/tough-cookie": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
"integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
"license": "BSD-3-Clause",
"dependencies": {
"tldts": "^6.1.32"
},
"engines": {
"node": ">=16"
}
},
"src/docs/node_modules/tr46": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
"license": "MIT",
"dependencies": {
"punycode": "^2.3.1"
},
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
}
},
"src/docs/node_modules/whatwg-encoding": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
"deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
"license": "MIT",
"dependencies": {
"iconv-lite": "0.6.3"
},
"engines": {
"node": ">=18"
}
},
"src/docs/node_modules/whatwg-url": {
"version": "14.2.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
"license": "MIT",
"dependencies": {
"tr46": "^5.1.0",
"webidl-conversions": "^7.0.0"
},
"engines": {
"node": ">=18"
}
},
"src/emulator": {
"version": "1.0.0",
"extraneous": true,
+127 -126
View File
@@ -54,138 +54,139 @@ const complete_ = async ({ req, res, user }) => {
// -----------------------------------------------------------------------//
// POST /login
// -----------------------------------------------------------------------//
router.post('/login', express.json(), body_parser_error_handler,
// Add diagnostic middleware to log captcha data
(req, res, next) => {
if ( process.env.DEBUG ) {
console.log('====== LOGIN CAPTCHA DIAGNOSTIC ======');
console.log('LOGIN REQUEST RECEIVED with captcha data:', {
hasCaptchaToken: !!req.body.captchaToken,
hasCaptchaAnswer: !!req.body.captchaAnswer,
captchaToken: req.body.captchaToken ? `${req.body.captchaToken.substring(0, 8) }...` : undefined,
captchaAnswer: req.body.captchaAnswer,
});
}
next();
}, requireCaptcha({ strictMode: true, eventType: 'login' }), async (req, res, next) => {
// either api. subdomain or no subdomain
if ( require('../helpers').subdomain(req) !== 'api' && require('../helpers').subdomain(req) !== '' )
{
next();
}
router.post('/login', express.json(), body_parser_error_handler, (req, res, next) => {
// Add diagnostic middleware to log captcha data
if ( process.env.DEBUG ) {
console.log('====== LOGIN CAPTCHA DIAGNOSTIC ======');
console.log('LOGIN REQUEST RECEIVED with captcha data:', {
hasCaptchaToken: !!req.body.captchaToken,
hasCaptchaAnswer: !!req.body.captchaAnswer,
captchaToken: req.body.captchaToken ? `${req.body.captchaToken.substring(0, 8) }...` : undefined,
captchaAnswer: req.body.captchaAnswer,
});
}
next();
}, requireCaptcha({ strictMode: true, eventType: 'login' }), async (req, res, next) => {
// either api. subdomain or no subdomain
if ( require('../helpers').subdomain(req) !== 'api' && require('../helpers').subdomain(req) !== '' ) {
next();
}
// modules
const bcrypt = require('bcrypt');
const validator = require('validator');
// modules
const bcrypt = require('bcrypt');
const validator = require('validator');
// either username or email must be provided
if ( !req.body.username && !req.body.email )
{
return res.status(400).send('Username or email is required.');
}
// password is required
else if ( ! req.body.password )
{
return res.status(400).send('Password is required.');
}
// password must be a string
else if ( typeof req.body.password !== 'string' && !(req.body.password instanceof String) )
{
return res.status(400).send('Password must be a string.');
}
// if password is too short it's invalid, no need to do a db lookup
else if ( req.body.password.length < config.min_pass_length )
{
return res.status(400).send('Invalid password.');
}
// username, if present, must be a string
else if ( req.body.username && typeof req.body.username !== 'string' && !(req.body.username instanceof String) )
{
return res.status(400).send('username must be a string.');
}
// if username doesn't pass regex test it's invalid anyway, no need to do DB lookup
else if ( req.body.username && !req.body.username.match(config.username_regex) )
{
return res.status(400).send('Invalid username.');
}
// email, if present, must be a string
else if ( req.body.email && typeof req.body.email !== 'string' && !(req.body.email instanceof String) )
{
return res.status(400).send('email must be a string.');
}
// if email is invalid, no need to do DB lookup anyway
else if ( req.body.email && !validator.isEmail(req.body.email) )
{
return res.status(400).send('Invalid email.');
}
// either username or email must be provided
if ( !req.body.username && !req.body.email ) {
return res.status(400).send('Username or email is required.');
}
// password is required
else if ( ! req.body.password )
{
return res.status(400).send('Password is required.');
}
// password must be a string
else if ( typeof req.body.password !== 'string' && !(req.body.password instanceof String) )
{
return res.status(400).send('Password must be a string.');
}
// if password is too short it's invalid, no need to do a db lookup
else if ( req.body.password.length < config.min_pass_length )
{
return res.status(400).send('Invalid password.');
}
// username, if present, must be a string
else if ( req.body.username && typeof req.body.username !== 'string' && !(req.body.username instanceof String) )
{
return res.status(400).send('username must be a string.');
}
// if username doesn't pass regex test it's invalid anyway, no need to do DB lookup
else if ( req.body.username && !req.body.username.match(config.username_regex) )
{
return res.status(400).send('Invalid username.');
}
// email, if present, must be a string
else if ( req.body.email && typeof req.body.email !== 'string' && !(req.body.email instanceof String) )
{
return res.status(400).send('email must be a string.');
}
// if email is invalid, no need to do DB lookup anyway
else if ( req.body.email && !validator.isEmail(req.body.email) )
{
return res.status(400).send('Invalid email.');
}
const svc_edgeRateLimit = req.services.get('edge-rate-limit');
if ( ! svc_edgeRateLimit.check('login') ) {
return res.status(429).send('Too many requests.');
}
/** @type {import('../services/abuse-prevention/EdgeRateLimitService').EdgeRateLimitService} */
const svc_edgeRateLimit = req.services.get('edge-rate-limit');
if ( ! svc_edgeRateLimit.check('login', true) ) {
return res.status(429).send('Too many requests.');
}
try {
let user;
// log in using username
if ( req.body.username ) {
user = await get_user({ username: req.body.username, cached: false });
if ( ! user )
{
return res.status(400).send('Username not found.');
}
}
// log in using email
else if ( validator.isEmail(req.body.email) ) {
user = await get_user({ email: req.body.email, cached: false });
if ( ! user )
{
return res.status(400).send('Email not found.');
}
}
if ( user.username === 'system' && config.allow_system_login !== true ) {
return res.status(400).send(
req.body.username
? 'Username not found.'
: 'Email not found.');
}
// is user suspended?
if ( user.suspended )
{
return res.status(401).send('This account is suspended.');
}
// pseudo user?
// todo make this better, maybe ask them to create an account or send them an activation link
if ( user.password === null )
{
return res.status(400).send('Incorrect password.');
}
// check password
if ( await bcrypt.compare(req.body.password, user.password) ) {
// We create a JWT that can ONLY be used on the endpoint that
// accepts the OTP code.
if ( user.otp_enabled ) {
const svc_token = req.services.get('token');
const otp_jwt_token = svc_token.sign('otp', {
user_uid: user.uuid,
}, { expiresIn: '5m' });
return res.status(202).send({
proceed: true,
next_step: 'otp',
otp_jwt_token: otp_jwt_token,
});
}
return await complete_({ req, res, user });
} else {
return res.status(400).send('Incorrect password.');
}
} catch (e) {
console.error(e);
return res.status(400).send(e);
}
try {
let user;
// log in using username
if ( req.body.username ) {
user = await get_user({ username: req.body.username, cached: false });
if ( ! user ) {
svc_edgeRateLimit.incr('login');
return res.status(400).send('Username not found.');
}
}
// log in using email
else if ( validator.isEmail(req.body.email) ) {
user = await get_user({ email: req.body.email, cached: false });
if ( ! user ) {
svc_edgeRateLimit.incr('login');
return res.status(400).send('Email not found.');
}
}
if ( user.username === 'system' && config.allow_system_login !== true ) {
svc_edgeRateLimit.incr('login');
return res.status(400).send(
req.body.username
? 'Username not found.'
: 'Email not found.');
}
// is user suspended?
if ( user.suspended ) {
svc_edgeRateLimit.incr('login');
return res.status(401).send('This account is suspended.');
}
// pseudo user?
// todo make this better, maybe ask them to create an account or send them an activation link
if ( user.password === null ) {
svc_edgeRateLimit.incr('login');
return res.status(400).send('Incorrect password.');
}
// check password
if ( await bcrypt.compare(req.body.password, user.password) ) {
// We create a JWT that can ONLY be used on the endpoint that
// accepts the OTP code.
if ( user.otp_enabled ) {
const svc_token = req.services.get('token');
const otp_jwt_token = svc_token.sign('otp', {
user_uid: user.uuid,
}, { expiresIn: '5m' });
return res.status(202).send({
proceed: true,
next_step: 'otp',
otp_jwt_token: otp_jwt_token,
});
}
return await complete_({ req, res, user });
} else {
svc_edgeRateLimit.incr('login');
return res.status(400).send('Incorrect password.');
}
} catch (e) {
console.error(e);
svc_edgeRateLimit.incr('login');
return res.status(400).send(e);
}
});
router.post('/login/otp', express.json(), body_parser_error_handler, requireCaptcha({ strictMode: true, eventType: 'login_otp' }), async (req, res, next) => {
// either api. subdomain or no subdomain
@@ -133,8 +133,8 @@ class EdgeRateLimitService extends BaseService {
asyncSafeSetInterval(() => this.cleanup(), 5 * MINUTE);
}
check (scope) {
if ( ! this.scopes.hasOwnProperty(scope) ) {
check (scope, noIncrease = false) {
if ( ! Object.prototype.hasOwnProperty.call(this.scopes, scope) ) {
throw new Error(`unrecognized rate-limit scope: ${quot(scope)}`);
}
const { window, limit } = this.scopes[scope];
@@ -162,11 +162,29 @@ class EdgeRateLimitService extends BaseService {
return false;
} else {
// Add current timestamp and allow the request
timestamps.push(now);
if ( ! noIncrease ) {
timestamps.push(now);
}
return true;
}
}
incr (scope) {
if ( ! Object.prototype.hasOwnProperty.call(this.scopes, scope) ) {
throw new Error(`unrecognized rate-limit scope: ${quot(scope)}`);
}
const requester = Context.get('requester');
const rl_identifier = requester.rl_identifier;
const key = `${scope}:${rl_identifier}`;
const now = Date.now();
if ( ! this.requests.has(key) ) {
this.requests.set(key, []);
}
const timestamps = this.requests.get(key);
timestamps.push(now);
}
/**
* Cleans up the rate limit request records by removing entries
* that have no associated timestamps. This method is intended