[add] redirectUriCheckController
This commit is contained in:
parent
d35af82aae
commit
9c4b53a6bc
27 changed files with 1235 additions and 161 deletions
32
.github/workflows/main.yml
vendored
Normal file
32
.github/workflows/main.yml
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
name: Build and Upload Caido Plugin
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
uses: oven-sh/setup-bun@v1
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
bun install
|
||||||
|
|
||||||
|
- name: Build plugin
|
||||||
|
run: |
|
||||||
|
bun run build
|
||||||
|
|
||||||
|
- name: Upload plugin artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: caido-plugin
|
||||||
|
path: dist
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1 +1,3 @@
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
|
/dist
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -6,4 +6,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
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.
|
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.
|
||||||
|
|
|
||||||
19
README.md
Normal file
19
README.md
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# caido-plugin-test
|
||||||
|
|
||||||
|
## To-Do
|
||||||
|
- [ ] PKCE 다운그래이드 https에서 작동 안하는 이슈 고치기
|
||||||
|
|
||||||
|
```log
|
||||||
|
2025-05-25T15:52:40.757475Z INFO actix-rt|system:0|arbiter:6 proxy|connect: Client connection (29e74afd-9006-445e-88a9-3fc5d4796af9)
|
||||||
|
2025-05-25T15:52:40.757530Z INFO actix-rt|system:0|arbiter:6 proxy|connect: Client connected for http://localhost:8787 (29e74afd-9006-445e-88a9-3fc5d4796af9)
|
||||||
|
2025-05-25T15:52:40.757562Z INFO actix-rt|system:0|arbiter:6 proxy|http1|logger: GET http://localhost/login (29e74afd-9006-445e-88a9-3fc5d4796af9)
|
||||||
|
2025-05-25T15:52:40.767186Z INFO actix-rt|system:0|arbiter:6 proxy|http1|logger: GET http://localhost:8787/login -> 302 361 (29e74afd-9006-445e-88a9-3fc5d4796af9)
|
||||||
|
2025-05-25T15:52:40.768696Z INFO actix-rt|system:0|arbiter:9 proxy|http1|logger: GET https://github.com/login/oauth/authorize?client_id=Ov23lixietSCQOHxPvcr&redirect_uri=http%3A%2F%2Flocalhost%3A8787%2Fcallback&scope=read%3Auser&state=bc11db571a4737d0&response_type=code&code_challenge=FtSdQsWI342PKH6BGgKYR6AOzW95LaS0jeVcwTmHaro&code_challenge_method=S256 (90f314dc-9480-4bd8-b7b6-5acba6b8bc7b)
|
||||||
|
2025-05-25T15:52:41.103596Z INFO actix-rt|system:0|arbiter:9 proxy|http1|logger: GET https://github.com/login/oauth/authorize?client_id=Ov23lixietSCQOHxPvcr&redirect_uri=http%3A%2F%2Flocalhost%3A8787%2Fcallback&scope=read%3Auser&state=bc11db571a4737d0&response_type=code&code_challenge=FtSdQsWI342PKH6BGgKYR6AOzW95LaS0jeVcwTmHaro&code_challenge_method=S256 -> 302 4927 (90f314dc-9480-4bd8-b7b6-5acba6b8bc7b)
|
||||||
|
2025-05-25T15:52:41.105944Z INFO actix-rt|system:0|arbiter:7 proxy|connect: Client connection (34585a00-9f9f-4c72-b087-2e9e92418dad)
|
||||||
|
2025-05-25T15:52:41.105993Z INFO actix-rt|system:0|arbiter:7 proxy|connect: Client connected for http://localhost:8787 (34585a00-9f9f-4c72-b087-2e9e92418dad)
|
||||||
|
2025-05-25T15:52:41.106023Z INFO actix-rt|system:0|arbiter:7 proxy|http1|logger: GET http://localhost/callback?code=10c34dcc4d3f7302e707&state=bc11db571a4737d0 (34585a00-9f9f-4c72-b087-2e9e92418dad)
|
||||||
|
2025-05-25T15:52:41.108270Z INFO plugin:65ad3a87-0257-4408-a9c7-e0885e04c162 js|sdk: [PKCEDowngradeCheck] Required PKCE parameters missing. Skipping.
|
||||||
|
2025-05-25T15:52:41.277387Z INFO plugin:65ad3a87-0257-4408-a9c7-e0885e04c162 js|sdk: [PKCEDowngradeCheck] No PKCE downgrade detected.
|
||||||
|
2025-05-25T15:52:41.686109Z INFO actix-rt|system:0|arbiter:7 proxy|http1|logger: GET http://localhost:8787/callback?code=10c34dcc4d3f7302e707&state=bc11db571a4737d0 -> 200 1582 (34585a00-9f9f-4c72-b087-2e9e92418dad)
|
||||||
|
```
|
||||||
502
bun.lock
Normal file
502
bun.lock
Normal file
|
|
@ -0,0 +1,502 @@
|
||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "caido-oauth",
|
||||||
|
"devDependencies": {
|
||||||
|
"@caido-community/dev": "^0.1.3",
|
||||||
|
"@caido/sdk-backend": "^0.48.1",
|
||||||
|
"typescript": "5.5.4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@caido-community/dev": ["@caido-community/dev@0.1.6", "", { "dependencies": { "@caido/plugin-manifest": "0.3.0", "chalk": "5.4.1", "chokidar": "4.0.3", "commander": "13.0.0", "express": "5.0.0", "glob": "11.0.1", "jiti": "2.4.2", "jszip": "3.10.1", "tsup": "8.3.5", "vite": "6.0.7", "ws": "8.18.0", "zod": "3.24.1" }, "bin": { "caido-dev": "dist/cli.js" } }, "sha512-WAWmPdEahh4e24sO4crt+nvqZryhKsy4yP5QYGoyUKqEYVAct5S/lI9fHdoIRQPJDSds3ayB6jgMKAlifd8BAg=="],
|
||||||
|
|
||||||
|
"@caido/plugin-manifest": ["@caido/plugin-manifest@0.3.0", "", { "dependencies": { "ajv": "^8.17.0" } }, "sha512-HRGHf65K2sfSdaEUwkCNDlurJ4zL0bOUg/Db4u6CrwjTTzmg2gOzP6SzLRj+69gWmxOm5LUjhInNHaMIRmQkHw=="],
|
||||||
|
|
||||||
|
"@caido/quickjs-types": ["@caido/quickjs-types@0.18.0", "", {}, "sha512-hRXUVdDvlhEhvkBoWWytoVS2j1KDVZa8dx2Q/KvWUQTR57U8EMSYE9iFgvPhu78gS8z+RF42Zcb7moNx4SDMlw=="],
|
||||||
|
|
||||||
|
"@caido/sdk-backend": ["@caido/sdk-backend@0.48.1", "", { "dependencies": { "@caido/quickjs-types": "0.18.0", "@caido/sdk-shared": "^0.1.0" } }, "sha512-JvFeOlSqAKbj3OenBn0LPtCNaOV0x6YtaAQijpvYfBJK32Nvbf924Z10bFVCu+Clc5A1qr7HcAvJ/8B/aRikWA=="],
|
||||||
|
|
||||||
|
"@caido/sdk-shared": ["@caido/sdk-shared@0.1.1", "", {}, "sha512-JAV5ajUqxZdXYPTmDEvIKBZon8I5uHq44ATj0Nj3BVpllRDUGY9kcBd+PXMD50+3lv1CvhR3/f6q24T0+4aVJQ=="],
|
||||||
|
|
||||||
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.24.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA=="],
|
||||||
|
|
||||||
|
"@esbuild/android-arm": ["@esbuild/android-arm@0.24.2", "", { "os": "android", "cpu": "arm" }, "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q=="],
|
||||||
|
|
||||||
|
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.24.2", "", { "os": "android", "cpu": "arm64" }, "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg=="],
|
||||||
|
|
||||||
|
"@esbuild/android-x64": ["@esbuild/android-x64@0.24.2", "", { "os": "android", "cpu": "x64" }, "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw=="],
|
||||||
|
|
||||||
|
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.24.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA=="],
|
||||||
|
|
||||||
|
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.24.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA=="],
|
||||||
|
|
||||||
|
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.24.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg=="],
|
||||||
|
|
||||||
|
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.24.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.24.2", "", { "os": "linux", "cpu": "arm" }, "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.24.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.24.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.24.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.24.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.24.2", "", { "os": "linux", "cpu": "x64" }, "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q=="],
|
||||||
|
|
||||||
|
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.24.2", "", { "os": "none", "cpu": "arm64" }, "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw=="],
|
||||||
|
|
||||||
|
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.24.2", "", { "os": "none", "cpu": "x64" }, "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw=="],
|
||||||
|
|
||||||
|
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.24.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A=="],
|
||||||
|
|
||||||
|
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.24.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA=="],
|
||||||
|
|
||||||
|
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.24.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.24.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.24.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.24.2", "", { "os": "win32", "cpu": "x64" }, "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg=="],
|
||||||
|
|
||||||
|
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
||||||
|
|
||||||
|
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
|
||||||
|
|
||||||
|
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||||
|
|
||||||
|
"@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||||
|
|
||||||
|
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.41.1", "", { "os": "android", "cpu": "arm" }, "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.41.1", "", { "os": "android", "cpu": "arm64" }, "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.41.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.41.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.41.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.41.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.41.1", "", { "os": "linux", "cpu": "arm" }, "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.41.1", "", { "os": "linux", "cpu": "arm" }, "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.41.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.41.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.41.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.41.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.41.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.41.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.41.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw=="],
|
||||||
|
|
||||||
|
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
|
||||||
|
|
||||||
|
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||||
|
|
||||||
|
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
||||||
|
|
||||||
|
"ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||||
|
|
||||||
|
"ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
||||||
|
|
||||||
|
"any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="],
|
||||||
|
|
||||||
|
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||||
|
|
||||||
|
"body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],
|
||||||
|
|
||||||
|
"brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||||
|
|
||||||
|
"bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="],
|
||||||
|
|
||||||
|
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||||
|
|
||||||
|
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||||
|
|
||||||
|
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||||
|
|
||||||
|
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||||
|
|
||||||
|
"chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="],
|
||||||
|
|
||||||
|
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||||
|
|
||||||
|
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||||
|
|
||||||
|
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||||
|
|
||||||
|
"commander": ["commander@13.0.0", "", {}, "sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ=="],
|
||||||
|
|
||||||
|
"consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
|
||||||
|
|
||||||
|
"content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="],
|
||||||
|
|
||||||
|
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
||||||
|
|
||||||
|
"cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
|
||||||
|
|
||||||
|
"cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
||||||
|
|
||||||
|
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
||||||
|
|
||||||
|
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||||
|
|
||||||
|
"debug": ["debug@4.3.6", "", { "dependencies": { "ms": "2.1.2" } }, "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg=="],
|
||||||
|
|
||||||
|
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||||
|
|
||||||
|
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||||
|
|
||||||
|
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
|
||||||
|
|
||||||
|
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
||||||
|
|
||||||
|
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||||
|
|
||||||
|
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||||
|
|
||||||
|
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||||
|
|
||||||
|
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||||
|
|
||||||
|
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||||
|
|
||||||
|
"esbuild": ["esbuild@0.24.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.24.2", "@esbuild/android-arm": "0.24.2", "@esbuild/android-arm64": "0.24.2", "@esbuild/android-x64": "0.24.2", "@esbuild/darwin-arm64": "0.24.2", "@esbuild/darwin-x64": "0.24.2", "@esbuild/freebsd-arm64": "0.24.2", "@esbuild/freebsd-x64": "0.24.2", "@esbuild/linux-arm": "0.24.2", "@esbuild/linux-arm64": "0.24.2", "@esbuild/linux-ia32": "0.24.2", "@esbuild/linux-loong64": "0.24.2", "@esbuild/linux-mips64el": "0.24.2", "@esbuild/linux-ppc64": "0.24.2", "@esbuild/linux-riscv64": "0.24.2", "@esbuild/linux-s390x": "0.24.2", "@esbuild/linux-x64": "0.24.2", "@esbuild/netbsd-arm64": "0.24.2", "@esbuild/netbsd-x64": "0.24.2", "@esbuild/openbsd-arm64": "0.24.2", "@esbuild/openbsd-x64": "0.24.2", "@esbuild/sunos-x64": "0.24.2", "@esbuild/win32-arm64": "0.24.2", "@esbuild/win32-ia32": "0.24.2", "@esbuild/win32-x64": "0.24.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA=="],
|
||||||
|
|
||||||
|
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
||||||
|
|
||||||
|
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
||||||
|
|
||||||
|
"express": ["express@5.0.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.0.1", "content-disposition": "^1.0.0", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "^1.2.1", "debug": "4.3.6", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "^2.0.0", "fresh": "2.0.0", "http-errors": "2.0.0", "merge-descriptors": "^2.0.0", "methods": "~1.1.2", "mime-types": "^3.0.0", "on-finished": "2.4.1", "once": "1.4.0", "parseurl": "~1.3.3", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "router": "^2.0.0", "safe-buffer": "5.2.1", "send": "^1.1.0", "serve-static": "^2.1.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "^2.0.0", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-V4UkHQc+B7ldh1YC84HCXHwf60M4BOMvp9rkvTUWCK5apqDC1Esnbid4wm6nFyVuDy8XMfETsJw5lsIGBWyo0A=="],
|
||||||
|
|
||||||
|
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||||
|
|
||||||
|
"fast-uri": ["fast-uri@3.0.6", "", {}, "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="],
|
||||||
|
|
||||||
|
"fdir": ["fdir@6.4.4", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg=="],
|
||||||
|
|
||||||
|
"finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="],
|
||||||
|
|
||||||
|
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||||
|
|
||||||
|
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||||
|
|
||||||
|
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
||||||
|
|
||||||
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
|
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||||
|
|
||||||
|
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||||
|
|
||||||
|
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||||
|
|
||||||
|
"glob": ["glob@11.0.1", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^4.0.1", "minimatch": "^10.0.0", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw=="],
|
||||||
|
|
||||||
|
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||||
|
|
||||||
|
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||||
|
|
||||||
|
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||||
|
|
||||||
|
"http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
|
||||||
|
|
||||||
|
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||||
|
|
||||||
|
"immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="],
|
||||||
|
|
||||||
|
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||||
|
|
||||||
|
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||||
|
|
||||||
|
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||||
|
|
||||||
|
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
||||||
|
|
||||||
|
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
||||||
|
|
||||||
|
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
|
|
||||||
|
"jackspeak": ["jackspeak@4.1.1", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ=="],
|
||||||
|
|
||||||
|
"jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
|
||||||
|
|
||||||
|
"joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="],
|
||||||
|
|
||||||
|
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||||
|
|
||||||
|
"jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="],
|
||||||
|
|
||||||
|
"lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
|
||||||
|
|
||||||
|
"lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
|
||||||
|
|
||||||
|
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
|
||||||
|
|
||||||
|
"load-tsconfig": ["load-tsconfig@0.2.5", "", {}, "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg=="],
|
||||||
|
|
||||||
|
"lodash.sortby": ["lodash.sortby@4.7.0", "", {}, "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="],
|
||||||
|
|
||||||
|
"lru-cache": ["lru-cache@11.1.0", "", {}, "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A=="],
|
||||||
|
|
||||||
|
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||||
|
|
||||||
|
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
||||||
|
|
||||||
|
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
||||||
|
|
||||||
|
"methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="],
|
||||||
|
|
||||||
|
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||||
|
|
||||||
|
"mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||||
|
|
||||||
|
"minimatch": ["minimatch@10.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ=="],
|
||||||
|
|
||||||
|
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
||||||
|
|
||||||
|
"ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
|
||||||
|
|
||||||
|
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
|
||||||
|
|
||||||
|
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
|
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||||
|
|
||||||
|
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||||
|
|
||||||
|
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||||
|
|
||||||
|
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||||
|
|
||||||
|
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||||
|
|
||||||
|
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
|
||||||
|
|
||||||
|
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
|
||||||
|
|
||||||
|
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
||||||
|
|
||||||
|
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||||
|
|
||||||
|
"path-scurry": ["path-scurry@2.0.0", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg=="],
|
||||||
|
|
||||||
|
"path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="],
|
||||||
|
|
||||||
|
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
|
|
||||||
|
"picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
|
||||||
|
|
||||||
|
"pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="],
|
||||||
|
|
||||||
|
"postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
|
||||||
|
|
||||||
|
"postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="],
|
||||||
|
|
||||||
|
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
|
||||||
|
|
||||||
|
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
||||||
|
|
||||||
|
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||||
|
|
||||||
|
"qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
|
||||||
|
|
||||||
|
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||||
|
|
||||||
|
"raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="],
|
||||||
|
|
||||||
|
"readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||||
|
|
||||||
|
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||||
|
|
||||||
|
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||||
|
|
||||||
|
"resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
|
||||||
|
|
||||||
|
"rollup": ["rollup@4.41.1", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.41.1", "@rollup/rollup-android-arm64": "4.41.1", "@rollup/rollup-darwin-arm64": "4.41.1", "@rollup/rollup-darwin-x64": "4.41.1", "@rollup/rollup-freebsd-arm64": "4.41.1", "@rollup/rollup-freebsd-x64": "4.41.1", "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", "@rollup/rollup-linux-arm-musleabihf": "4.41.1", "@rollup/rollup-linux-arm64-gnu": "4.41.1", "@rollup/rollup-linux-arm64-musl": "4.41.1", "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-musl": "4.41.1", "@rollup/rollup-linux-s390x-gnu": "4.41.1", "@rollup/rollup-linux-x64-gnu": "4.41.1", "@rollup/rollup-linux-x64-musl": "4.41.1", "@rollup/rollup-win32-arm64-msvc": "4.41.1", "@rollup/rollup-win32-ia32-msvc": "4.41.1", "@rollup/rollup-win32-x64-msvc": "4.41.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw=="],
|
||||||
|
|
||||||
|
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
|
||||||
|
|
||||||
|
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||||
|
|
||||||
|
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||||
|
|
||||||
|
"send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
|
||||||
|
|
||||||
|
"serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="],
|
||||||
|
|
||||||
|
"setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="],
|
||||||
|
|
||||||
|
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||||
|
|
||||||
|
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||||
|
|
||||||
|
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||||
|
|
||||||
|
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
||||||
|
|
||||||
|
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
|
||||||
|
|
||||||
|
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
|
||||||
|
|
||||||
|
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||||
|
|
||||||
|
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||||
|
|
||||||
|
"source-map": ["source-map@0.8.0-beta.0", "", { "dependencies": { "whatwg-url": "^7.0.0" } }, "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA=="],
|
||||||
|
|
||||||
|
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||||
|
|
||||||
|
"statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
|
||||||
|
|
||||||
|
"string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||||
|
|
||||||
|
"string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
||||||
|
"string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||||
|
|
||||||
|
"strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||||
|
|
||||||
|
"strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
||||||
|
"sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="],
|
||||||
|
|
||||||
|
"thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="],
|
||||||
|
|
||||||
|
"thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="],
|
||||||
|
|
||||||
|
"tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
|
||||||
|
|
||||||
|
"tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="],
|
||||||
|
|
||||||
|
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||||
|
|
||||||
|
"tr46": ["tr46@1.0.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="],
|
||||||
|
|
||||||
|
"tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="],
|
||||||
|
|
||||||
|
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
|
||||||
|
|
||||||
|
"tsup": ["tsup@8.3.5", "", { "dependencies": { "bundle-require": "^5.0.0", "cac": "^6.7.14", "chokidar": "^4.0.1", "consola": "^3.2.3", "debug": "^4.3.7", "esbuild": "^0.24.0", "joycon": "^3.1.1", "picocolors": "^1.1.1", "postcss-load-config": "^6.0.1", "resolve-from": "^5.0.0", "rollup": "^4.24.0", "source-map": "0.8.0-beta.0", "sucrase": "^3.35.0", "tinyexec": "^0.3.1", "tinyglobby": "^0.2.9", "tree-kill": "^1.2.2" }, "peerDependencies": { "@microsoft/api-extractor": "^7.36.0", "@swc/core": "^1", "postcss": "^8.4.12", "typescript": ">=4.5.0" }, "optionalPeers": ["@microsoft/api-extractor", "@swc/core", "postcss", "typescript"], "bin": { "tsup": "dist/cli-default.js", "tsup-node": "dist/cli-node.js" } }, "sha512-Tunf6r6m6tnZsG9GYWndg0z8dEV7fD733VBFzFJ5Vcm1FtlXB8xBD/rtrBi2a3YKEV7hHtxiZtW5EAVADoe1pA=="],
|
||||||
|
|
||||||
|
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@5.5.4", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q=="],
|
||||||
|
|
||||||
|
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||||
|
|
||||||
|
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||||
|
|
||||||
|
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
|
||||||
|
|
||||||
|
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||||
|
|
||||||
|
"vite": ["vite@6.0.7", "", { "dependencies": { "esbuild": "^0.24.2", "postcss": "^8.4.49", "rollup": "^4.23.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ=="],
|
||||||
|
|
||||||
|
"webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="],
|
||||||
|
|
||||||
|
"whatwg-url": ["whatwg-url@7.1.0", "", { "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg=="],
|
||||||
|
|
||||||
|
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||||
|
|
||||||
|
"wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
||||||
|
|
||||||
|
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||||
|
|
||||||
|
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||||
|
|
||||||
|
"ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="],
|
||||||
|
|
||||||
|
"zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
|
||||||
|
|
||||||
|
"body-parser/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||||
|
|
||||||
|
"body-parser/qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
|
||||||
|
|
||||||
|
"finalhandler/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||||
|
|
||||||
|
"readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||||
|
|
||||||
|
"router/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||||
|
|
||||||
|
"send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||||
|
|
||||||
|
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
||||||
|
"string_decoder/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||||
|
|
||||||
|
"strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
|
"sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
|
||||||
|
|
||||||
|
"sucrase/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
|
||||||
|
|
||||||
|
"tsup/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||||
|
|
||||||
|
"wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
|
|
||||||
|
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
||||||
|
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
||||||
|
"body-parser/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"finalhandler/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"router/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
|
"sucrase/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||||
|
|
||||||
|
"sucrase/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
||||||
|
"sucrase/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||||
|
|
||||||
|
"tsup/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||||
|
|
||||||
|
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
|
"sucrase/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
dist/plugin_package.zip
vendored
BIN
dist/plugin_package.zip
vendored
Binary file not shown.
21
dist/plugin_package/manifest.json
vendored
21
dist/plugin_package/manifest.json
vendored
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"id": "oauth-vuln-detector",
|
|
||||||
"name": "OAuth Vuln Detector",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "Detects OAuth misconfiguration.",
|
|
||||||
"author": {
|
|
||||||
"name": "caterpii",
|
|
||||||
"email": "dlaha171@gmail.com",
|
|
||||||
"url": "https://github.com/katerpii"
|
|
||||||
},
|
|
||||||
"links": {},
|
|
||||||
"plugins": [
|
|
||||||
{
|
|
||||||
"id": "oauth-backend",
|
|
||||||
"kind": "backend",
|
|
||||||
"name": "backend",
|
|
||||||
"entrypoint": "oauth-backend/index.js",
|
|
||||||
"runtime": "javascript"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -12,4 +12,4 @@
|
||||||
"@caido/sdk-backend": "^0.46.0",
|
"@caido/sdk-backend": "^0.46.0",
|
||||||
"typescript": "5.5.4"
|
"typescript": "5.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
145
packages/backend/dist/index.js
vendored
145
packages/backend/dist/index.js
vendored
|
|
@ -1,20 +1,110 @@
|
||||||
// packages/backend/src/index.ts
|
// packages/backend/src/controller/PKCECheck.ts
|
||||||
|
import { RequestSpec } from "caido:utils";
|
||||||
|
var PKCECheck = class {
|
||||||
|
async test(sdk, req) {
|
||||||
|
const method = req.getMethod();
|
||||||
|
if (method !== "GET") {
|
||||||
|
sdk.console.log("[PKCEDowngradeCheck] Not a GET request. Skipping.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const query = req.getQuery();
|
||||||
|
const searchParams = new URLSearchParams(query);
|
||||||
|
const requiredKeys = ["client_id", "response_type", "code_challenge", "code_challenge_method"];
|
||||||
|
if (!requiredKeys.every((key) => searchParams.has(key))) {
|
||||||
|
sdk.console.log("[PKCEDowngradeCheck] Required PKCE parameters missing. Skipping.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const url = req.getUrl();
|
||||||
|
const isOpenID = searchParams.get("scope")?.includes("openid") || url.includes("id_token");
|
||||||
|
const methodVal = searchParams.get("code_challenge_method");
|
||||||
|
const challengeVal = searchParams.get("code_challenge");
|
||||||
|
if (!methodVal || !challengeVal) {
|
||||||
|
sdk.console.log("[PKCEDowngradeCheck] code_challenge or method missing. Skipping.");
|
||||||
|
await sdk.findings.create({
|
||||||
|
title: isOpenID ? "[WARN] OpenID Flow PKCE Parameters Missing" : "[WARN] OAuth2 Flow PKCE Parameters Missing",
|
||||||
|
description: `PKCE parameters are missing or incomplete for ${url}. This may indicate a misconfiguration.`,
|
||||||
|
request: req,
|
||||||
|
reporter: "PKCE Checker"
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (methodVal === "plain") {
|
||||||
|
sdk.console.log("[PKCEDowngradeCheck] code_challenge_method is 'plain'. Skipping.");
|
||||||
|
await sdk.findings.create({
|
||||||
|
title: isOpenID ? "[WARN] OpenID Flow PKCE Method is 'plain'" : "[WARN] OAuth2 Flow PKCE Method is 'plain'",
|
||||||
|
description: `PKCE method is set to 'plain' for ${url}. This may indicate a downgrade vulnerability.`,
|
||||||
|
request: req,
|
||||||
|
reporter: "PKCE Checker"
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
searchParams.delete("code_challenge");
|
||||||
|
searchParams.delete("code_challenge_method");
|
||||||
|
const downgradedQuery = searchParams.toString();
|
||||||
|
const scheme = req.getUrl().startsWith("https") ? "https" : "http";
|
||||||
|
const downgradedUrl = `${scheme}://${req.getHost()}:${req.getPort()}${req.getPath()}?${downgradedQuery}`;
|
||||||
|
sdk.console.log(`${req.getHost()} Original URL: ` + url);
|
||||||
|
sdk.console.log(`${req.getHost()} Downgraded URL: ` + downgradedUrl);
|
||||||
|
try {
|
||||||
|
const spec = new RequestSpec(downgradedUrl);
|
||||||
|
spec.setBody(req.getBody());
|
||||||
|
for (const [key, value] of Object.entries(req.getHeaders())) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
spec.setHeader(key, value.join(", "));
|
||||||
|
} else {
|
||||||
|
spec.setHeader(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spec.setHost(req.getHost());
|
||||||
|
spec.setMethod(req.getMethod());
|
||||||
|
spec.setPath(req.getPath());
|
||||||
|
spec.setQuery(downgradedQuery);
|
||||||
|
spec.setTls(req.getTls());
|
||||||
|
spec.setPort(req.getPort());
|
||||||
|
let sendDowngradedRequest = await sdk.requests.send(spec);
|
||||||
|
if (sendDowngradedRequest.response) {
|
||||||
|
let domain = spec.getHost();
|
||||||
|
let port = spec.getPort();
|
||||||
|
let path2 = spec.getPath();
|
||||||
|
let query2 = spec.getQuery();
|
||||||
|
let id = sendDowngradedRequest.response.getId();
|
||||||
|
let code = sendDowngradedRequest.response.getCode();
|
||||||
|
sdk.console.log(`REQ ${id}: ${domain}:${port}${path2}${query2} received a status code of ${code}`);
|
||||||
|
}
|
||||||
|
if (sendDowngradedRequest.response?.getCode() === 302) {
|
||||||
|
await sdk.findings.create({
|
||||||
|
title: isOpenID ? "[CRITICAL] OpenID Flow PKCE Downgrade Vulnerability" : "[CRITICAL] OAuth2 Flow PKCE Downgrade Vulnerability",
|
||||||
|
description: `The request to ${url} is vulnerable to a PKCE downgrade attack. This may indicate a configuration error.`,
|
||||||
|
request: req,
|
||||||
|
reporter: "PKCE Checker"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
sdk.console.error(`PKCE downgrade check failed for ${url}: ${String(err)}`);
|
||||||
|
}
|
||||||
|
sdk.console.log("[PKCEDowngradeCheck] No PKCE downgrade detected.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// packages/backend/src/controller/redirectUriCheck.ts
|
||||||
import { promises as fs } from "fs";
|
import { promises as fs } from "fs";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import os from "os";
|
import os from "os";
|
||||||
var requestMap = /* @__PURE__ */ new Map();
|
var redirectUriCheck = class {
|
||||||
function init(sdk) {
|
requestMap = /* @__PURE__ */ new Map();
|
||||||
sdk.events.onInterceptRequest(async (sdk2, req) => {
|
// constructor(private sdk: SDK<any>) {}
|
||||||
|
async onRequest(sdk, req) {
|
||||||
try {
|
try {
|
||||||
const urlString = req.getUrl();
|
const urlString = req.getUrl();
|
||||||
const url = new URL(urlString);
|
const url = new URL(urlString);
|
||||||
sdk2.console.log(`[OAuthPlugin] Intercepted request: ${urlString}`);
|
sdk.console.log(`[OAuthPlugin] Intercepted request: ${urlString}`);
|
||||||
if (!url.pathname.includes("/authorize") && !url.pathname.includes("/auth")) return;
|
if (!url.pathname.includes("/authorize") && !url.pathname.includes("/auth")) return;
|
||||||
const params = new URLSearchParams(url.search);
|
const params = new URLSearchParams(url.search);
|
||||||
const redirectUri = params.get("redirect_uri");
|
const redirectUri = params.get("redirect_uri");
|
||||||
if (!redirectUri) return;
|
if (!redirectUri) return;
|
||||||
const reqId = req.getId();
|
const reqId = req.getId();
|
||||||
requestMap.set(reqId, redirectUri);
|
this.requestMap.set(reqId, redirectUri);
|
||||||
const clientId = params.get("client_id") ?? "(missing)";
|
const clientId = params.get("client_id") ?? "(missing)";
|
||||||
const responseType = params.get("response_type") ?? "(missing)";
|
const responseType = params.get("response_type") ?? "(missing)";
|
||||||
const isScan = params.has("scan");
|
const isScan = params.has("scan");
|
||||||
|
|
@ -29,14 +119,14 @@ function init(sdk) {
|
||||||
const filePath = path.join(os.tmpdir(), "oauth-fuzz-input.json");
|
const filePath = path.join(os.tmpdir(), "oauth-fuzz-input.json");
|
||||||
await fs.writeFile(filePath, JSON.stringify(output, null, 2));
|
await fs.writeFile(filePath, JSON.stringify(output, null, 2));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await sdk2.findings.create({
|
await sdk.findings.create({
|
||||||
title: "[fs] Write Failed",
|
title: "[fs] Write Failed",
|
||||||
description: `Could not write to file: ${err}`,
|
description: `Could not write to file: ${err}`,
|
||||||
request: req,
|
request: req,
|
||||||
reporter: "oauth-open-redirect-detector"
|
reporter: "oauth-open-redirect-detector"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await sdk2.findings.create({
|
await sdk.findings.create({
|
||||||
title: "[ ] OAuth2 Authorization Request Collected",
|
title: "[ ] OAuth2 Authorization Request Collected",
|
||||||
description: `client_id: ${clientId}
|
description: `client_id: ${clientId}
|
||||||
redirect_uri: ${redirectUri}
|
redirect_uri: ${redirectUri}
|
||||||
|
|
@ -45,10 +135,10 @@ response_type: ${responseType}`,
|
||||||
reporter: "oauth-open-redirect-detector"
|
reporter: "oauth-open-redirect-detector"
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
sdk2.console.error(`Error in onInterceptRequest: ${err}`);
|
sdk.console.error(`Error in onRequest: ${err}`);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
sdk.events.onInterceptResponse(async (sdk2, req, resp) => {
|
async onResponse(sdk, req, resp) {
|
||||||
try {
|
try {
|
||||||
const reqId = req.getId();
|
const reqId = req.getId();
|
||||||
const url = new URL(req.getUrl());
|
const url = new URL(req.getUrl());
|
||||||
|
|
@ -57,26 +147,43 @@ response_type: ${responseType}`,
|
||||||
const params = new URLSearchParams(url.search);
|
const params = new URLSearchParams(url.search);
|
||||||
const isScan = params.has("scan");
|
const isScan = params.has("scan");
|
||||||
if (!isScan) {
|
if (!isScan) {
|
||||||
requestMap.delete(reqId);
|
this.requestMap.delete(reqId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (status >= 300 && status < 400 && location) {
|
if (status >= 300 && status < 400 && location) {
|
||||||
const redirectUri = requestMap.get(reqId) ?? "(unknown)";
|
const redirectUri = this.requestMap.get(reqId) ?? "(unknown)";
|
||||||
await sdk2.findings.create({
|
await sdk.findings.create({
|
||||||
title: "[+] Redirect URI Misconfiguration Detected",
|
title: "[+] Redirect URI Misconfiguration Detected",
|
||||||
description: `Status: ${status}
|
description: `Status: ${status}
|
||||||
Location: ${location}
|
Location: ${location}
|
||||||
Original Redirect URI: ${redirectUri}
|
Request URL: ${url.href}
|
||||||
Request URL: ${url.href}`,
|
Redirect URI: ${redirectUri}`,
|
||||||
request: req,
|
request: req,
|
||||||
reporter: "oauth-open-redirect-detector"
|
reporter: "oauth-open-redirect-detector"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
requestMap.delete(reqId);
|
this.requestMap.delete(reqId);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
sdk2.console.error(`Error in onInterceptResponse: ${err}`);
|
sdk.console.error(`Error in onResponse: ${err}`);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// packages/backend/src/index.ts
|
||||||
|
var pkceCheckController = new PKCECheck();
|
||||||
|
var redirectUriCheckController = new redirectUriCheck();
|
||||||
|
function init(sdk) {
|
||||||
|
sdk.events.onInterceptRequest(
|
||||||
|
async (sdk2, req) => {
|
||||||
|
await redirectUriCheckController.onRequest(sdk2, req);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
sdk.events.onInterceptResponse(
|
||||||
|
async (sdk2, req, resp) => {
|
||||||
|
await pkceCheckController.test(sdk2, req);
|
||||||
|
await redirectUriCheckController.onResponse(sdk2, req, resp);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
export {
|
export {
|
||||||
init
|
init
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,4 @@
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"build": "vite build"
|
"build": "vite build"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
144
packages/backend/src/controller/PKCECheck.ts
Normal file
144
packages/backend/src/controller/PKCECheck.ts
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
import type { SDK } from "caido:plugin";
|
||||||
|
import { Body, RequestSpec, type Request } from "caido:utils";
|
||||||
|
|
||||||
|
export class PKCECheck {
|
||||||
|
async test(sdk: SDK, req: Request): Promise<boolean> {
|
||||||
|
const method = req.getMethod();
|
||||||
|
if (method !== "GET") {
|
||||||
|
sdk.console.log("[PKCEDowngradeCheck] Not a GET request. Skipping.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = req.getQuery();
|
||||||
|
const searchParams = new URLSearchParams(query);
|
||||||
|
const requiredKeys = ["client_id", "response_type", "code_challenge", "code_challenge_method"];
|
||||||
|
|
||||||
|
if (!requiredKeys.every((key) => searchParams.has(key))) {
|
||||||
|
sdk.console.log("[PKCEDowngradeCheck] Required PKCE parameters missing. Skipping.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = req.getUrl();
|
||||||
|
const isOpenID = searchParams.get("scope")?.includes("openid") || url.includes("id_token");
|
||||||
|
const methodVal = searchParams.get("code_challenge_method");
|
||||||
|
const challengeVal = searchParams.get("code_challenge");
|
||||||
|
|
||||||
|
if (!methodVal || !challengeVal) {
|
||||||
|
sdk.console.log("[PKCEDowngradeCheck] code_challenge or method missing. Skipping.");
|
||||||
|
await sdk.findings.create({
|
||||||
|
title: isOpenID
|
||||||
|
? "[WARN] OpenID Flow PKCE Parameters Missing"
|
||||||
|
: "[WARN] OAuth2 Flow PKCE Parameters Missing",
|
||||||
|
description: `PKCE parameters are missing or incomplete for ${url}. This may indicate a misconfiguration.`,
|
||||||
|
request: req,
|
||||||
|
reporter: "PKCE Checker",
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methodVal === "plain") {
|
||||||
|
sdk.console.log("[PKCEDowngradeCheck] code_challenge_method is 'plain'. Skipping.");
|
||||||
|
await sdk.findings.create({
|
||||||
|
title: isOpenID
|
||||||
|
? "[WARN] OpenID Flow PKCE Method is 'plain'"
|
||||||
|
: "[WARN] OAuth2 Flow PKCE Method is 'plain'",
|
||||||
|
description: `PKCE method is set to 'plain' for ${url}. This may indicate a downgrade vulnerability.`,
|
||||||
|
request: req,
|
||||||
|
reporter: "PKCE Checker",
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove PKCE parameters to simulate a downgraded request
|
||||||
|
searchParams.delete("code_challenge");
|
||||||
|
searchParams.delete("code_challenge_method");
|
||||||
|
const downgradedQuery = searchParams.toString();
|
||||||
|
const scheme = req.getUrl().startsWith("https") ? "https" : "http";
|
||||||
|
const downgradedUrl = `${scheme}://${req.getHost()}:${req.getPort()}${req.getPath()}?${downgradedQuery}`;
|
||||||
|
|
||||||
|
sdk.console.log(`${req.getHost()} Original URL: ` + url);
|
||||||
|
sdk.console.log(`${req.getHost()} Downgraded URL: ` + downgradedUrl);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Use Caido Replay SDK to replay the original request
|
||||||
|
const spec = new RequestSpec(downgradedUrl);
|
||||||
|
spec.setBody(req.getBody() as Body);
|
||||||
|
for (const [key, value] of Object.entries(req.getHeaders())) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
spec.setHeader(key, value.join(', ')); // or another suitable delimiter
|
||||||
|
} else {
|
||||||
|
spec.setHeader(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spec.setHost(req.getHost());
|
||||||
|
spec.setMethod(req.getMethod());
|
||||||
|
spec.setPath(req.getPath());
|
||||||
|
spec.setQuery(downgradedQuery);
|
||||||
|
spec.setTls(req.getTls());
|
||||||
|
spec.setPort(req.getPort());
|
||||||
|
|
||||||
|
let sendDowngradedRequest = await sdk.requests.send(spec);
|
||||||
|
|
||||||
|
if (sendDowngradedRequest.response) {
|
||||||
|
let domain = spec.getHost();
|
||||||
|
let port = spec.getPort();
|
||||||
|
let path = spec.getPath();
|
||||||
|
let query = spec.getQuery();
|
||||||
|
let id = sendDowngradedRequest.response.getId();
|
||||||
|
let code = sendDowngradedRequest.response.getCode();
|
||||||
|
sdk.console.log(`REQ ${id}: ${domain}:${port}${path}${query} received a status code of ${code}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendDowngradedRequest.response?.getCode() === 302) {
|
||||||
|
await sdk.findings.create({
|
||||||
|
title: isOpenID
|
||||||
|
? "[CRITICAL] OpenID Flow PKCE Downgrade Vulnerability"
|
||||||
|
: "[CRITICAL] OAuth2 Flow PKCE Downgrade Vulnerability",
|
||||||
|
description: `The request to ${url} is vulnerable to a PKCE downgrade attack. This may indicate a configuration error.`,
|
||||||
|
request: req,
|
||||||
|
reporter: "PKCE Checker",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sdk.console.log(`${req.getHost()} Original Status: ` + resOriginal.status);
|
||||||
|
sdk.console.log(`${req.getHost()} Downgraded Status: ` + resDowngraded.status);
|
||||||
|
|
||||||
|
sdk.console.log(`${req.getHost()} Original Headers: ` + JSON.stringify(resOriginal.headers));
|
||||||
|
sdk.console.log(`${req.getHost()} Downgraded Headers: ` + JSON.stringify(resDowngraded.headers));
|
||||||
|
|
||||||
|
// Caido Dev Docs 기준으로, 리다이렉트된 URL은 Response 객체의 url 속성에 저장되어 있음
|
||||||
|
const locationOriginal = resOriginal.url ?? "";
|
||||||
|
const locationDowngraded = resDowngraded.url ?? "";
|
||||||
|
|
||||||
|
sdk.console.log(`${req.getHost()} Original Location: ` + locationOriginal);
|
||||||
|
sdk.console.log(`${req.getHost()} Downgraded Location: ` + locationDowngraded);
|
||||||
|
|
||||||
|
const statusEqual = resOriginal.status === resDowngraded.status;
|
||||||
|
const codeInRedirects = locationOriginal.includes("code=") && locationDowngraded.includes("code=");
|
||||||
|
|
||||||
|
if (statusEqual && codeInRedirects) {
|
||||||
|
const title = isOpenID
|
||||||
|
? "[CRITICAL] OpenID Flow PKCE Downgraded to Plaintext"
|
||||||
|
: "[CRITICAL] OAuth2 Flow PKCE Downgraded to Plaintext";
|
||||||
|
const reference = isOpenID
|
||||||
|
? "https://openid.net/specs/openid-igov-oauth2-1_0-02.html#rfc.section.3.1.7"
|
||||||
|
: "https://datatracker.ietf.org/doc/html/rfc7636";
|
||||||
|
|
||||||
|
await sdk.findings.create({
|
||||||
|
title,
|
||||||
|
description: `PKCE downgrade detected for ${url}.\n\nDowngraded URL: ${downgradedUrl}\n\nRedirect contained code=.\n\nReference: ${reference}`,
|
||||||
|
request: req,
|
||||||
|
reporter: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}*/
|
||||||
|
} catch (err) {
|
||||||
|
sdk.console.error(`PKCE downgrade check failed for ${url}: ${String(err)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
sdk.console.log("[PKCEDowngradeCheck] No PKCE downgrade detected.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
48
packages/backend/src/controller/authZCodeGrant.ts
Normal file
48
packages/backend/src/controller/authZCodeGrant.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import type { Request } from "caido:utils";
|
||||||
|
|
||||||
|
export class AuthZCodeGrantController {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
isAuthZReq(req: Request) {
|
||||||
|
// const path = req.getPath();
|
||||||
|
const query = req.getQuery();
|
||||||
|
// const raw = req.getRaw().toString();
|
||||||
|
|
||||||
|
// 쿼리에 client_id and response_type=code 포함 확인
|
||||||
|
if (query.includes("client_id=") && query.includes("response_type=code")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSendCodeToClient(req: Request) {
|
||||||
|
const path = req.getPath();
|
||||||
|
const query = req.getQuery();
|
||||||
|
|
||||||
|
// code & state 쌍 또는 path에 &code= 또는 쿼리로 code= 유사 일치 확인
|
||||||
|
if (
|
||||||
|
(query.includes("code=") && query.includes("state=")) ||
|
||||||
|
path.includes("&code=") ||
|
||||||
|
/code=%/i.test(query)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
testReq(req: Request) {
|
||||||
|
if (this.isAuthZReq(req)) {
|
||||||
|
return "isAuthZReq";
|
||||||
|
}
|
||||||
|
if (this.isSendCodeToClient(req)) {
|
||||||
|
return "isSendCodeToClient";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// isAccessTokenReq(req: Response) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
||||||
40
packages/backend/src/controller/implictGrant.ts
Normal file
40
packages/backend/src/controller/implictGrant.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import type { Request } from "caido:utils";
|
||||||
|
|
||||||
|
export class ImplicitGrantController {
|
||||||
|
isImplicitGrantReq(req: Request) {
|
||||||
|
const query = req.getQuery();
|
||||||
|
// const raw = req.getRaw().toString();
|
||||||
|
|
||||||
|
// 쿼리에 client_id and response_type=token 포함 확인
|
||||||
|
if (query.includes("client_id=") && query.includes("response_type=token")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSendTokenToClient(req: Request) {
|
||||||
|
const path = req.getPath();
|
||||||
|
const query = req.getQuery();
|
||||||
|
|
||||||
|
if (
|
||||||
|
(query.includes("access_token=") && query.includes("state=")) ||
|
||||||
|
path.includes("&access_token=") ||
|
||||||
|
/access_token=%/i.test(query)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
testReq(req: Request) {
|
||||||
|
if (this.isImplicitGrantReq(req)) {
|
||||||
|
return "isImplicitGrantReq";
|
||||||
|
}
|
||||||
|
if (this.isSendTokenToClient(req)) {
|
||||||
|
return "isSendTokenToClient";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
packages/backend/src/controller/nonceCheck.ts
Normal file
33
packages/backend/src/controller/nonceCheck.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import type { Request } from "caido:utils";
|
||||||
|
import { TokenLeakCheck } from "./tokenLeakCheck";
|
||||||
|
|
||||||
|
export class NonceCheckController{
|
||||||
|
/**
|
||||||
|
* 응답이 OIDC(OpenID Connect) 플로우인지 확인하는 메서드
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static isOidcFlow(req: Request): boolean {
|
||||||
|
if(TokenLeakCheck.extractIdToken(req)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static isNonceCheckRequest(req: Request): boolean {
|
||||||
|
const id_token = decodeIdToken(req);
|
||||||
|
|
||||||
|
// 1. nonce 파라미터가 포함된 요청인지 확인
|
||||||
|
if (id_token.includes("nonce=")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeIdToken(req: Request): string {
|
||||||
|
// Implement actual decoding logic here. For now, return an empty string or mock value.
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,83 +1,95 @@
|
||||||
// packages/backend/src/index.ts
|
// oauth-plugin.ts
|
||||||
|
import type { SDK } from "caido:plugin";
|
||||||
|
import type { Request, Response } from "caido:utils";
|
||||||
import { promises as fs } from "fs";
|
import { promises as fs } from "fs";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import os from "os";
|
import os from "os";
|
||||||
var requestMap = /* @__PURE__ */ new Map();
|
|
||||||
function init(sdk) {
|
export class redirectUriCheck {
|
||||||
sdk.events.onInterceptRequest(async (sdk2, req) => {
|
private requestMap = new Map<string, string>();
|
||||||
|
|
||||||
|
// constructor(private sdk: SDK<any>) {}
|
||||||
|
|
||||||
|
public async onRequest(sdk: SDK, req: Request) {
|
||||||
try {
|
try {
|
||||||
const urlString = req.getUrl();
|
const urlString = req.getUrl();
|
||||||
const url = new URL(urlString);
|
const url = new URL(urlString);
|
||||||
sdk2.console.log(`[OAuthPlugin] Intercepted request: ${urlString}`);
|
sdk.console.log(`[OAuthPlugin] Intercepted request: ${urlString}`);
|
||||||
|
|
||||||
if (!url.pathname.includes("/authorize") && !url.pathname.includes("/auth")) return;
|
if (!url.pathname.includes("/authorize") && !url.pathname.includes("/auth")) return;
|
||||||
|
|
||||||
const params = new URLSearchParams(url.search);
|
const params = new URLSearchParams(url.search);
|
||||||
const redirectUri = params.get("redirect_uri");
|
const redirectUri = params.get("redirect_uri");
|
||||||
if (!redirectUri) return;
|
if (!redirectUri) return;
|
||||||
|
|
||||||
const reqId = req.getId();
|
const reqId = req.getId();
|
||||||
requestMap.set(reqId, redirectUri);
|
this.requestMap.set(reqId, redirectUri);
|
||||||
|
|
||||||
const clientId = params.get("client_id") ?? "(missing)";
|
const clientId = params.get("client_id") ?? "(missing)";
|
||||||
const responseType = params.get("response_type") ?? "(missing)";
|
const responseType = params.get("response_type") ?? "(missing)";
|
||||||
const isScan = params.has("scan");
|
const isScan = params.has("scan");
|
||||||
if (isScan) return;
|
if (isScan) return;
|
||||||
|
|
||||||
const output = {
|
const output = {
|
||||||
original_url: urlString,
|
original_url: urlString,
|
||||||
client_id: clientId,
|
client_id: clientId,
|
||||||
redirect_uri: redirectUri,
|
redirect_uri: redirectUri,
|
||||||
response_type: responseType
|
response_type: responseType,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const filePath = path.join(os.tmpdir(), "oauth-fuzz-input.json");
|
const filePath = path.join(os.tmpdir(), "oauth-fuzz-input.json");
|
||||||
await fs.writeFile(filePath, JSON.stringify(output, null, 2));
|
await fs.writeFile(filePath, JSON.stringify(output, null, 2));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await sdk2.findings.create({
|
await sdk.findings.create({
|
||||||
title: "[fs] Write Failed",
|
title: "[fs] Write Failed",
|
||||||
description: `Could not write to file: ${err}`,
|
description: `Could not write to file: ${err}`,
|
||||||
request: req,
|
request: req,
|
||||||
reporter: "oauth-open-redirect-detector"
|
reporter: "oauth-open-redirect-detector"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await sdk2.findings.create({
|
|
||||||
|
await sdk.findings.create({
|
||||||
title: "[ ] OAuth2 Authorization Request Collected",
|
title: "[ ] OAuth2 Authorization Request Collected",
|
||||||
description: `client_id: ${clientId}
|
description: `client_id: ${clientId}\nredirect_uri: ${redirectUri}\nresponse_type: ${responseType}`,
|
||||||
redirect_uri: ${redirectUri}
|
|
||||||
response_type: ${responseType}`,
|
|
||||||
request: req,
|
request: req,
|
||||||
reporter: "oauth-open-redirect-detector"
|
reporter: "oauth-open-redirect-detector"
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
sdk2.console.error(`Error in onInterceptRequest: ${err}`);
|
sdk.console.error(`Error in onRequest: ${err}`);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
sdk.events.onInterceptResponse(async (sdk2, req, resp) => {
|
|
||||||
|
public async onResponse(sdk: SDK, req: Request, resp: Response) {
|
||||||
try {
|
try {
|
||||||
const reqId = req.getId();
|
const reqId = req.getId();
|
||||||
const url = new URL(req.getUrl());
|
const url = new URL(req.getUrl());
|
||||||
const status = resp.getCode();
|
const status = resp.getCode();
|
||||||
const location = resp.getHeader("location")?.[0];
|
const location = resp.getHeader("location")?.[0];
|
||||||
|
|
||||||
const params = new URLSearchParams(url.search);
|
const params = new URLSearchParams(url.search);
|
||||||
const isScan = params.has("scan");
|
const isScan = params.has("scan");
|
||||||
|
|
||||||
if (!isScan) {
|
if (!isScan) {
|
||||||
requestMap.delete(reqId);
|
this.requestMap.delete(reqId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status >= 300 && status < 400 && location) {
|
if (status >= 300 && status < 400 && location) {
|
||||||
const redirectUri = requestMap.get(reqId) ?? "(unknown)";
|
const redirectUri = this.requestMap.get(reqId) ?? "(unknown)";
|
||||||
await sdk2.findings.create({
|
|
||||||
|
await sdk.findings.create({
|
||||||
title: "[+] Redirect URI Misconfiguration Detected",
|
title: "[+] Redirect URI Misconfiguration Detected",
|
||||||
description: `Status: ${status}
|
description: `Status: ${status}\nLocation: ${location}\nRequest URL: ${url.href}\nRedirect URI: ${redirectUri}`,
|
||||||
Location: ${location}
|
|
||||||
Original Redirect URI: ${redirectUri}
|
|
||||||
Request URL: ${url.href}`,
|
|
||||||
request: req,
|
request: req,
|
||||||
reporter: "oauth-open-redirect-detector"
|
reporter: "oauth-open-redirect-detector",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
requestMap.delete(reqId);
|
|
||||||
|
this.requestMap.delete(reqId);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
sdk2.console.error(`Error in onInterceptResponse: ${err}`);
|
sdk.console.error(`Error in onResponse: ${err}`);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
export {
|
|
||||||
init
|
|
||||||
};
|
|
||||||
42
packages/backend/src/controller/tokenLeakCheck.ts
Normal file
42
packages/backend/src/controller/tokenLeakCheck.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import type { Request } from "caido:utils";
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
|
||||||
|
export class TokenLeakCheck {
|
||||||
|
public static extractIdToken(req: Request): string | null {
|
||||||
|
// 1. Authorization 헤더 확인\\
|
||||||
|
const header = req.getHeaders() as Record<string, string | string[] | undefined>;
|
||||||
|
const authHeader = header["authorization"] || header["Authorization"];
|
||||||
|
if (typeof authHeader === "string" && authHeader.startsWith("Bearer ")) {
|
||||||
|
return authHeader.slice(7).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 쿼리 파라미터
|
||||||
|
const query = req.getQuery();
|
||||||
|
if (query && typeof query === "object" && "id_token" in query) {
|
||||||
|
return (query as Record<string, any>).id_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. POST 바디 안에 id_token이 있을 경우
|
||||||
|
const rawBody = req.getRaw();
|
||||||
|
const body = rawBody ? rawBody.toString() : "";
|
||||||
|
const match = body.match(/id_token=([^&\s]+)/);
|
||||||
|
if (match && typeof match[1] === "string") {
|
||||||
|
return decodeURIComponent(match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static decodeIdToken(req: Request): Record<string, any> | null {
|
||||||
|
const token = this.extractIdToken(req);
|
||||||
|
if (!token) return null;
|
||||||
|
|
||||||
|
const decoded = jwt.decode(token, { complete: true });
|
||||||
|
if (!decoded || typeof decoded !== "object") return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
header: decoded.header,
|
||||||
|
payload: decoded.payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,94 +1,55 @@
|
||||||
import type { SDK, DefineAPI } from "caido:plugin";
|
import type { SDK, DefineAPI } from "caido:plugin";
|
||||||
import type { Request, Response } from "caido:utils";
|
import type { Request, Response } from "caido:utils";
|
||||||
import { promises as fs } from "fs";
|
// import { ImplicitGrantController } from "./controller/implictGrant";
|
||||||
import * as path from "path";
|
// import { AuthZCodeGrantController } from "./controller/authZCodeGrant";
|
||||||
import os from "os";
|
import { PKCECheck } from "./controller/PKCECheck";
|
||||||
|
import { redirectUriCheck } from "./controller/redirectUriCheck";
|
||||||
|
|
||||||
export type API = DefineAPI<{}>;
|
export type API = DefineAPI<{}>;
|
||||||
|
|
||||||
const requestMap = new Map<string, string>();
|
// const implicitGrantController = new ImplicitGrantController();
|
||||||
|
// const authZCodeGrantController = new AuthZCodeGrantController();
|
||||||
|
const pkceCheckController = new PKCECheck();
|
||||||
|
const redirectUriCheckController = new redirectUriCheck();
|
||||||
|
|
||||||
export function init(sdk: SDK<API>) {
|
export function init(sdk: SDK<API>) {
|
||||||
|
// sdk.events.onInterceptRequest(async (sdk, req: Request) => {
|
||||||
|
// const result = csrfCheck.checker(req);
|
||||||
|
|
||||||
sdk.events.onInterceptRequest(async (sdk, req: Request) => {
|
// if (result) {
|
||||||
try {
|
// await sdk.findings.create({
|
||||||
const urlString = req.getUrl();
|
// title: "Possible SSO Request Detected",
|
||||||
const url = new URL(urlString);
|
// description: `SSO-related parameters detected in request:\n\n${req.getMethod()} ${req.getUrl()} : ${result}`,
|
||||||
sdk.console.log(`[OAuthPlugin] Intercepted request: ${urlString}`);
|
// request: req,
|
||||||
|
// reporter: "",
|
||||||
if (!url.pathname.includes("/authorize") && !url.pathname.includes("/auth")) return;
|
// });
|
||||||
|
// }
|
||||||
const params = new URLSearchParams(url.search);
|
// });
|
||||||
const redirectUri = params.get("redirect_uri");
|
sdk.events.onInterceptRequest(
|
||||||
if (!redirectUri) return;
|
async(sdk: SDK<DefineAPI<{}>, {}>, req: Request) => {
|
||||||
|
await redirectUriCheckController.onRequest(sdk, req);
|
||||||
const reqId = req.getId();
|
|
||||||
requestMap.set(reqId, redirectUri);
|
|
||||||
|
|
||||||
const clientId = params.get("client_id") ?? "(missing)";
|
|
||||||
const responseType = params.get("response_type") ?? "(missing)";
|
|
||||||
const isScan = params.has("scan");
|
|
||||||
if (isScan) return;
|
|
||||||
|
|
||||||
const output = {
|
|
||||||
original_url: urlString,
|
|
||||||
client_id: clientId,
|
|
||||||
redirect_uri: redirectUri,
|
|
||||||
response_type: responseType,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
const filePath = path.join(os.tmpdir(), "oauth-fuzz-input.json");
|
|
||||||
await fs.writeFile(filePath, JSON.stringify(output, null, 2));
|
|
||||||
} catch (err) {
|
|
||||||
await sdk.findings.create({
|
|
||||||
title: "[fs] Write Failed",
|
|
||||||
description: `Could not write to file: ${err}`,
|
|
||||||
request: req,
|
|
||||||
reporter: "oauth-open-redirect-detector"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await sdk.findings.create({
|
|
||||||
title: "[ ] OAuth2 Authorization Request Collected",
|
|
||||||
description: `client_id: ${clientId}\nredirect_uri: ${redirectUri}\nresponse_type: ${responseType}`,
|
|
||||||
request: req,
|
|
||||||
reporter: "oauth-open-redirect-detector"
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
sdk.console.error(`Error in onInterceptRequest: ${err}`);
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
sdk.events.onInterceptResponse(async (sdk, req: Request, resp: Response) => {
|
sdk.events.onInterceptResponse(
|
||||||
try {
|
async (sdk: SDK<DefineAPI<{}>, {}>, req: Request, resp: Response) => {
|
||||||
const reqId = req.getId();
|
await pkceCheckController.test(sdk, req);
|
||||||
const url = new URL(req.getUrl());
|
await redirectUriCheckController.onResponse(sdk, req, resp);
|
||||||
const status = resp.getCode();
|
|
||||||
const location = resp.getHeader("location")?.[0];
|
|
||||||
|
|
||||||
const params = new URLSearchParams(url.search);
|
// sdk.events.onInterceptRequest(async (sdk, req: Request) => {
|
||||||
const isScan = params.has("scan");
|
// const result =
|
||||||
|
// authZCodeGrantController.testReq(req) ||
|
||||||
|
// implicitGrantController.testReq(req);
|
||||||
|
|
||||||
if (!isScan) {
|
// if (result) {
|
||||||
requestMap.delete(reqId);
|
// await pkceCheckController.test(sdk, req);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status >= 300 && status < 400 && location) {
|
// await sdk.findings.create({
|
||||||
const redirectUri = requestMap.get(reqId) ?? "(unknown)";
|
// title: "Possible SSO Request Detected",
|
||||||
|
// description: `SSO-related parameters detected in request:\n\n${req.getMethod()} ${req.getUrl()} : ${result}`,
|
||||||
await sdk.findings.create({
|
// request: req,
|
||||||
title: "[+] Redirect URI Misconfiguration Detected",
|
// reporter: "",
|
||||||
description: `Status: ${status}\nLocation: ${location}\nOriginal Redirect URI: ${redirectUri}\nRequest URL: ${url.href}`,
|
// });
|
||||||
request: req,
|
|
||||||
reporter: "oauth-open-redirect-detector",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
requestMap.delete(reqId);
|
|
||||||
} catch (err) {
|
|
||||||
sdk.console.error(`Error in onInterceptResponse: ${err}`);
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -5,4 +5,4 @@
|
||||||
"lib": ["ESNext", "DOM"]
|
"lib": ["ESNext", "DOM"]
|
||||||
},
|
},
|
||||||
"include": ["./src/**/*.ts"]
|
"include": ["./src/**/*.ts"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
playground/PKCEDowngrade/.env.example
Normal file
2
playground/PKCEDowngrade/.env.example
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
GITHUB_CLIENT_ID=
|
||||||
|
GITHUB_CLIENT_SECRET=
|
||||||
2
playground/PKCEDowngrade/.gitignore
vendored
Normal file
2
playground/PKCEDowngrade/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# deps
|
||||||
|
node_modules/
|
||||||
11
playground/PKCEDowngrade/README.md
Normal file
11
playground/PKCEDowngrade/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
To install dependencies:
|
||||||
|
```sh
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
To run:
|
||||||
|
```sh
|
||||||
|
bun run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
open http://localhost:3000
|
||||||
25
playground/PKCEDowngrade/bun.lock
Normal file
25
playground/PKCEDowngrade/bun.lock
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "PKCEDowngrade",
|
||||||
|
"dependencies": {
|
||||||
|
"hono": "^4.7.10",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@types/bun": ["@types/bun@1.2.14", "", { "dependencies": { "bun-types": "1.2.14" } }, "sha512-VsFZKs8oKHzI7zwvECiAJ5oSorWndIWEVhfbYqZd4HI/45kzW7PN2Rr5biAzvGvRuNmYLSANY+H59ubHq8xw7Q=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@22.15.21", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.2.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-Kuh4Ub28ucMRWeiUUWMHsT9Wcbr4H3kLIO72RZZElSDxSu7vpetRvxIUDUaW6QtaIeixIpm7OXtNnZPf82EzwA=="],
|
||||||
|
|
||||||
|
"hono": ["hono@4.7.10", "", {}, "sha512-QkACju9MiN59CKSY5JsGZCYmPZkA6sIW6OFCUp7qDjZu6S6KHtJHhAc9Uy9mV9F8PJ1/HQ3ybZF2yjCa/73fvQ=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
12
playground/PKCEDowngrade/package.json
Normal file
12
playground/PKCEDowngrade/package.json
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"name": "PKCEDowngrade",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "bun run --hot src/index.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hono": "^4.7.10"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest"
|
||||||
|
}
|
||||||
|
}
|
||||||
94
playground/PKCEDowngrade/src/index.ts
Normal file
94
playground/PKCEDowngrade/src/index.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { Hono } from 'hono'
|
||||||
|
import { randomBytes, createHash } from 'crypto'
|
||||||
|
import { Buffer } from 'buffer'
|
||||||
|
|
||||||
|
const app = new Hono()
|
||||||
|
|
||||||
|
// In-memory PKCE store (should use Redis or similar in production)
|
||||||
|
const pkceStore = new Map<string, string>()
|
||||||
|
|
||||||
|
const generateCodeVerifier = () => {
|
||||||
|
return randomBytes(32).toString('hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateCodeChallenge = (verifier: string) => {
|
||||||
|
const hash = createHash('sha256').update(verifier).digest()
|
||||||
|
return hash.toString('base64url')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: Redirect to GitHub with PKCE
|
||||||
|
app.get('/login', (c) => {
|
||||||
|
const codeVerifier = generateCodeVerifier()
|
||||||
|
const codeChallenge = generateCodeChallenge(codeVerifier)
|
||||||
|
const state = randomBytes(8).toString('hex')
|
||||||
|
|
||||||
|
pkceStore.set(state, codeVerifier)
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
client_id: process.env.GITHUB_CLIENT_ID!,
|
||||||
|
redirect_uri: 'http://localhost:8787/callback',
|
||||||
|
scope: 'read:user',
|
||||||
|
state,
|
||||||
|
response_type: 'code',
|
||||||
|
code_challenge: codeChallenge,
|
||||||
|
code_challenge_method: 'S256',
|
||||||
|
})
|
||||||
|
|
||||||
|
return c.redirect(`https://github.com/login/oauth/authorize?${params}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Step 2: GitHub redirects back here
|
||||||
|
app.get('/callback', async (c) => {
|
||||||
|
const url = new URL(c.req.url)
|
||||||
|
const code = url.searchParams.get('code')
|
||||||
|
const state = url.searchParams.get('state')
|
||||||
|
|
||||||
|
if (!code || !state) {
|
||||||
|
return c.text('Missing code or state', 400)
|
||||||
|
}
|
||||||
|
|
||||||
|
const codeVerifier = pkceStore.get(state)
|
||||||
|
if (!codeVerifier) {
|
||||||
|
return c.text('Invalid or expired state', 400)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Exchange code + verifier for token
|
||||||
|
const tokenRes = await fetch('https://github.com/login/oauth/access_token', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
client_id: process.env.GITHUB_CLIENT_ID,
|
||||||
|
client_secret: process.env.GITHUB_CLIENT_SECRET,
|
||||||
|
code,
|
||||||
|
redirect_uri: 'http://localhost:8787/callback',
|
||||||
|
code_verifier: codeVerifier,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const tokenData = await tokenRes.json()
|
||||||
|
if (!tokenData.access_token) {
|
||||||
|
return c.text('Failed to get access token', 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Use token to fetch user profile
|
||||||
|
const userRes = await fetch('https://api.github.com/user', {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${tokenData.access_token}`,
|
||||||
|
'User-Agent': 'hono-app',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = await userRes.json()
|
||||||
|
return c.json({
|
||||||
|
message: 'GitHub login successful!',
|
||||||
|
user,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
port: 8787,
|
||||||
|
fetch: app.fetch,
|
||||||
|
}
|
||||||
7
playground/PKCEDowngrade/tsconfig.json
Normal file
7
playground/PKCEDowngrade/tsconfig.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "hono/jsx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
packages:
|
packages:
|
||||||
- 'packages/*'
|
- 'packages/*'
|
||||||
|
|
|
||||||
|
|
@ -21,4 +21,4 @@
|
||||||
|
|
||||||
"baseUrl": "."
|
"baseUrl": "."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue