mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 13:00:39 +00:00
feat: enhance deployment process and update dependencies
- Updated the deployment script to build the web application before deploying. - Upgraded Wrangler dependency from 4.61.1 to 4.69.0. feat: add import item limit and request body size limit - Introduced a new limit for the maximum total items allowed in a single import (5000). - Set a hard body size limit for JSON API endpoints (25 MB). feat: validate KDF parameters during registration and password change - Added validation for KDF parameters to ensure compliance with Bitwarden's minimum requirements. - Enhanced error handling for invalid KDF parameters during user registration and password change. feat: clean up R2 files on user deletion - Implemented cleanup of R2 files associated with user attachments and sends before deleting user metadata. feat: verify folder ownership when creating or updating ciphers - Added checks to ensure that users cannot reference folders owned by other users when creating or updating ciphers. fix: handle corrupted cipher data gracefully - Improved error handling when retrieving ciphers from the database to avoid crashes due to corrupted data. feat: increment send access count atomically - Added a method to atomically increment the access count for sends and return whether the update was successful. fix: enforce request body size limits - Implemented checks to reject oversized request bodies for non-file upload paths. fix: update error handling for database initialization - Enhanced error logging for database initialization failures while providing a generic message to clients. feat: enhance security with Content Security Policy - Added a Content Security Policy to the web application to improve security against XSS attacks. fix: remove plaintext TOTP secret from localStorage - Updated the TOTP enabling process to remove the plaintext secret from localStorage after it is stored on the server. fix: ensure only PBKDF2 hash is sent for public send access - Modified the public send access payload to ensure only the PBKDF2 hash is sent, never the plaintext password.
This commit is contained in:
@@ -85,12 +85,12 @@ npx wrangler d1 create nodewarden-db
|
||||
npx wrangler r2 bucket create nodewarden-attachments
|
||||
|
||||
# 部署
|
||||
npx wrangler deploy
|
||||
npm run deploy
|
||||
|
||||
# 需更新时重新拉取仓库,重新部署即可,无需创建云资源
|
||||
git clone https://github.com/shuaiplus/NodeWarden.git
|
||||
cd NodeWarden
|
||||
npx wrangler deploy
|
||||
npm run deploy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
+2
-2
@@ -87,12 +87,12 @@ npx wrangler d1 create nodewarden-db
|
||||
npx wrangler r2 bucket create nodewarden-attachments
|
||||
|
||||
# Deploy
|
||||
npx wrangler deploy
|
||||
npm run deploy
|
||||
|
||||
# To update later: re-clone and re-deploy — no need to recreate cloud resources
|
||||
git clone https://github.com/shuaiplus/NodeWarden.git
|
||||
cd NodeWarden
|
||||
npx wrangler deploy
|
||||
npm run deploy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Generated
+156
-157
@@ -22,7 +22,7 @@
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.3.1",
|
||||
"wrangler": "^4.61.1"
|
||||
"wrangler": "^4.69.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@@ -383,14 +383,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/unenv-preset": {
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/unenv-preset/-/unenv-preset-2.12.0.tgz",
|
||||
"integrity": "sha512-NK4vN+2Z/GbfGS4BamtbbVk1rcu5RmqaYGiyHJQrA09AoxdZPHDF3W/EhgI0YSK8p3vRo/VNCtbSJFPON7FWMQ==",
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/unenv-preset/-/unenv-preset-2.14.0.tgz",
|
||||
"integrity": "sha512-XKAkWhi1nBdNsSEoNG9nkcbyvfUrSjSf+VYVPfOto3gLTZVc3F4g6RASCMh6IixBKCG2yDgZKQIHGKtjcnLnKg==",
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"unenv": "2.0.0-rc.24",
|
||||
"workerd": "^1.20260115.0"
|
||||
"workerd": "^1.20260218.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"workerd": {
|
||||
@@ -399,9 +399,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workerd-darwin-64": {
|
||||
"version": "1.20260128.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260128.0.tgz",
|
||||
"integrity": "sha512-XJN8zWWNG3JwAUqqwMLNKJ9fZfdlQkx/zTTHW/BB8wHat9LjKD6AzxqCu432YmfjR+NxEKCzUOxMu1YOxlVxmg==",
|
||||
"version": "1.20260305.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260305.0.tgz",
|
||||
"integrity": "sha512-chhKOpymo0Eh9J3nymrauMqKGboCc4uz/j0gA1G4gioMnKsN2ZDKJ+qjRZDnCoVGy8u2C4pxlmyIfsXCAfIzhQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -416,9 +416,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workerd-darwin-arm64": {
|
||||
"version": "1.20260128.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260128.0.tgz",
|
||||
"integrity": "sha512-vKnRcmnm402GQ5DOdfT5H34qeR2m07nhnTtky8mTkNWP+7xmkz32AMdclwMmfO/iX9ncyKwSqmml2wPG32eq/w==",
|
||||
"version": "1.20260305.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260305.0.tgz",
|
||||
"integrity": "sha512-K9aG2OQk5bBfOP+fyGPqLcqZ9OR3ra6uwnxJ8f2mveq2A2LsCI7ZeGxQiAj75Ti80ytH/gJffZIx4Np2JtU3aQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -433,9 +433,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workerd-linux-64": {
|
||||
"version": "1.20260128.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260128.0.tgz",
|
||||
"integrity": "sha512-RiaR+Qugof/c6oI5SagD2J5wJmIfI8wQWaV2Y9905Raj6sAYOFaEKfzkKnoLLLNYb4NlXicBrffJi1j7R/ypUA==",
|
||||
"version": "1.20260305.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260305.0.tgz",
|
||||
"integrity": "sha512-tt7XUoIw/cYFeGbkPkcZ6XX1aZm26Aju/4ih+DXxOosbBeGshFSrNJDBfAKKOvkjsAZymJ+WWVDBU+hmNaGfwA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -450,9 +450,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workerd-linux-arm64": {
|
||||
"version": "1.20260128.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260128.0.tgz",
|
||||
"integrity": "sha512-U39U9vcXLXYDbrJ112Q7D0LDUUnM54oXfAxPgrL2goBwio7Z6RnsM25TRvm+Q06F4+FeDOC4D51JXlFHb9t1OA==",
|
||||
"version": "1.20260305.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260305.0.tgz",
|
||||
"integrity": "sha512-72QTkY5EzylmvCZ8ZTrnJ9DctmQsfSof1OKyOWqu/pv/B2yACfuPMikq8RpPxvVu7hhS0ztGP6ZvXz72Htq4Zg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -467,9 +467,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workerd-windows-64": {
|
||||
"version": "1.20260128.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260128.0.tgz",
|
||||
"integrity": "sha512-fdJwSqRkJsAJFJ7+jy0th2uMO6fwaDA8Ny6+iFCssfzlNkc4dP/twXo+3F66FMLMe/6NIqjzVts0cpiv7ERYbQ==",
|
||||
"version": "1.20260305.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260305.0.tgz",
|
||||
"integrity": "sha512-BA0uaQPOaI2F6mJtBDqplGnQQhpXCzwEMI33p/TnDxtSk9u8CGIfBFuI6uqo8mJ6ijIaPjeBLGOn2CiRMET4qg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -484,9 +484,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workers-types": {
|
||||
"version": "4.20260131.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/workers-types/-/workers-types-4.20260131.0.tgz",
|
||||
"integrity": "sha512-ELgvb2mp68Al50p+FmpgCO2hgU5o4tmz8pi7kShN+cRXc0UZoEdxpDIikR0CeT7b3tV7wlnEnsUzd0UoJLS0oQ==",
|
||||
"version": "4.20260305.0",
|
||||
"resolved": "https://registry.npmmirror.com/@cloudflare/workers-types/-/workers-types-4.20260305.0.tgz",
|
||||
"integrity": "sha512-sCgPFnQ03SVpC2OVW8wysONLZW/A8hlp9Mq2ckG/h1oId4kr9NawA6vUiOmOjCWRn2hIohejBYVQ+Vu20rCdKA==",
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"peer": true
|
||||
@@ -516,9 +516,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz",
|
||||
"integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
|
||||
"integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -533,9 +533,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.27.0.tgz",
|
||||
"integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
|
||||
"integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -550,9 +550,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz",
|
||||
"integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -567,9 +567,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.27.0.tgz",
|
||||
"integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -584,9 +584,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz",
|
||||
"integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -601,9 +601,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz",
|
||||
"integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -618,9 +618,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz",
|
||||
"integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -635,9 +635,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz",
|
||||
"integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -652,9 +652,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz",
|
||||
"integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
|
||||
"integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -669,9 +669,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz",
|
||||
"integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -686,9 +686,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz",
|
||||
"integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
|
||||
"integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -703,9 +703,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz",
|
||||
"integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
|
||||
"integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -720,9 +720,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz",
|
||||
"integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
|
||||
"integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
@@ -737,9 +737,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz",
|
||||
"integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
|
||||
"integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -754,9 +754,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz",
|
||||
"integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
|
||||
"integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -771,9 +771,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz",
|
||||
"integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
|
||||
"integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -788,9 +788,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz",
|
||||
"integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -805,9 +805,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz",
|
||||
"integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -822,9 +822,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz",
|
||||
"integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -839,9 +839,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz",
|
||||
"integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -856,9 +856,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz",
|
||||
"integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -873,9 +873,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz",
|
||||
"integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -890,9 +890,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz",
|
||||
"integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -907,9 +907,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz",
|
||||
"integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -924,9 +924,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz",
|
||||
"integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
|
||||
"integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -941,9 +941,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz",
|
||||
"integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -958,9 +958,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/colour": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@img/colour/-/colour-1.0.0.tgz",
|
||||
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/@img/colour/-/colour-1.1.0.tgz",
|
||||
"integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2337,9 +2337,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.27.0.tgz",
|
||||
"integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==",
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.27.3.tgz",
|
||||
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
@@ -2350,32 +2350,32 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.27.0",
|
||||
"@esbuild/android-arm": "0.27.0",
|
||||
"@esbuild/android-arm64": "0.27.0",
|
||||
"@esbuild/android-x64": "0.27.0",
|
||||
"@esbuild/darwin-arm64": "0.27.0",
|
||||
"@esbuild/darwin-x64": "0.27.0",
|
||||
"@esbuild/freebsd-arm64": "0.27.0",
|
||||
"@esbuild/freebsd-x64": "0.27.0",
|
||||
"@esbuild/linux-arm": "0.27.0",
|
||||
"@esbuild/linux-arm64": "0.27.0",
|
||||
"@esbuild/linux-ia32": "0.27.0",
|
||||
"@esbuild/linux-loong64": "0.27.0",
|
||||
"@esbuild/linux-mips64el": "0.27.0",
|
||||
"@esbuild/linux-ppc64": "0.27.0",
|
||||
"@esbuild/linux-riscv64": "0.27.0",
|
||||
"@esbuild/linux-s390x": "0.27.0",
|
||||
"@esbuild/linux-x64": "0.27.0",
|
||||
"@esbuild/netbsd-arm64": "0.27.0",
|
||||
"@esbuild/netbsd-x64": "0.27.0",
|
||||
"@esbuild/openbsd-arm64": "0.27.0",
|
||||
"@esbuild/openbsd-x64": "0.27.0",
|
||||
"@esbuild/openharmony-arm64": "0.27.0",
|
||||
"@esbuild/sunos-x64": "0.27.0",
|
||||
"@esbuild/win32-arm64": "0.27.0",
|
||||
"@esbuild/win32-ia32": "0.27.0",
|
||||
"@esbuild/win32-x64": "0.27.0"
|
||||
"@esbuild/aix-ppc64": "0.27.3",
|
||||
"@esbuild/android-arm": "0.27.3",
|
||||
"@esbuild/android-arm64": "0.27.3",
|
||||
"@esbuild/android-x64": "0.27.3",
|
||||
"@esbuild/darwin-arm64": "0.27.3",
|
||||
"@esbuild/darwin-x64": "0.27.3",
|
||||
"@esbuild/freebsd-arm64": "0.27.3",
|
||||
"@esbuild/freebsd-x64": "0.27.3",
|
||||
"@esbuild/linux-arm": "0.27.3",
|
||||
"@esbuild/linux-arm64": "0.27.3",
|
||||
"@esbuild/linux-ia32": "0.27.3",
|
||||
"@esbuild/linux-loong64": "0.27.3",
|
||||
"@esbuild/linux-mips64el": "0.27.3",
|
||||
"@esbuild/linux-ppc64": "0.27.3",
|
||||
"@esbuild/linux-riscv64": "0.27.3",
|
||||
"@esbuild/linux-s390x": "0.27.3",
|
||||
"@esbuild/linux-x64": "0.27.3",
|
||||
"@esbuild/netbsd-arm64": "0.27.3",
|
||||
"@esbuild/netbsd-x64": "0.27.3",
|
||||
"@esbuild/openbsd-arm64": "0.27.3",
|
||||
"@esbuild/openbsd-x64": "0.27.3",
|
||||
"@esbuild/openharmony-arm64": "0.27.3",
|
||||
"@esbuild/sunos-x64": "0.27.3",
|
||||
"@esbuild/win32-arm64": "0.27.3",
|
||||
"@esbuild/win32-ia32": "0.27.3",
|
||||
"@esbuild/win32-x64": "0.27.3"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
@@ -2541,16 +2541,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare": {
|
||||
"version": "4.20260128.0",
|
||||
"resolved": "https://registry.npmmirror.com/miniflare/-/miniflare-4.20260128.0.tgz",
|
||||
"integrity": "sha512-AVCn3vDRY+YXu1sP4mRn81ssno6VUqxo29uY2QVfgxXU2TMLvhRIoGwm7RglJ3Gzfuidit5R86CMQ6AvdFTGAw==",
|
||||
"version": "4.20260305.0",
|
||||
"resolved": "https://registry.npmmirror.com/miniflare/-/miniflare-4.20260305.0.tgz",
|
||||
"integrity": "sha512-jVhtKJtiwaZa3rI+WgoLvSJmEazDsoUmAPYRUmEe2VO6VSbvkhbnDRm+dsPbYRatgNIExwrpqG1rv96jHiSb0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "0.8.1",
|
||||
"sharp": "^0.34.5",
|
||||
"undici": "7.18.2",
|
||||
"workerd": "1.20260128.0",
|
||||
"workerd": "1.20260305.0",
|
||||
"ws": "8.18.0",
|
||||
"youch": "4.1.0-beta.10"
|
||||
},
|
||||
@@ -2780,9 +2780,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@@ -3113,13 +3113,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/workerd": {
|
||||
"version": "1.20260128.0",
|
||||
"resolved": "https://registry.npmmirror.com/workerd/-/workerd-1.20260128.0.tgz",
|
||||
"integrity": "sha512-EhLJGptSGFi8AEErLiamO3PoGpbRqL+v4Ve36H2B38VxmDgFOSmDhfepBnA14sCQzGf1AEaoZX2DCwZsmO74yQ==",
|
||||
"version": "1.20260305.0",
|
||||
"resolved": "https://registry.npmmirror.com/workerd/-/workerd-1.20260305.0.tgz",
|
||||
"integrity": "sha512-JkhfCLU+w+KbQmZ9k49IcDYc78GBo7eG8Mir8E2+KVjR7otQAmpcLlsous09YLh8WQ3Bt3Mi6/WMStvMAPukeA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"workerd": "bin/workerd"
|
||||
},
|
||||
@@ -3127,11 +3126,11 @@
|
||||
"node": ">=16"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@cloudflare/workerd-darwin-64": "1.20260128.0",
|
||||
"@cloudflare/workerd-darwin-arm64": "1.20260128.0",
|
||||
"@cloudflare/workerd-linux-64": "1.20260128.0",
|
||||
"@cloudflare/workerd-linux-arm64": "1.20260128.0",
|
||||
"@cloudflare/workerd-windows-64": "1.20260128.0"
|
||||
"@cloudflare/workerd-darwin-64": "1.20260305.0",
|
||||
"@cloudflare/workerd-darwin-arm64": "1.20260305.0",
|
||||
"@cloudflare/workerd-linux-64": "1.20260305.0",
|
||||
"@cloudflare/workerd-linux-arm64": "1.20260305.0",
|
||||
"@cloudflare/workerd-windows-64": "1.20260305.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wouter": {
|
||||
@@ -3149,20 +3148,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler": {
|
||||
"version": "4.61.1",
|
||||
"resolved": "https://registry.npmmirror.com/wrangler/-/wrangler-4.61.1.tgz",
|
||||
"integrity": "sha512-hfYQ16VLPkNi8xE1/V3052S2stM5e+vq3Idpt83sXoDC3R7R1CLgMkK6M6+Qp3G+9GVDNyHCkvohMPdfFTaD4Q==",
|
||||
"version": "4.69.0",
|
||||
"resolved": "https://registry.npmmirror.com/wrangler/-/wrangler-4.69.0.tgz",
|
||||
"integrity": "sha512-EmVfIM65I5b4ITHe3Y9R7zQyf4NUBQ1leStakMlWiVR9n6VlDwuEltyQI2l3i0JciDnWyR3uqe+T6C08ivniTQ==",
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@cloudflare/kv-asset-handler": "0.4.2",
|
||||
"@cloudflare/unenv-preset": "2.12.0",
|
||||
"@cloudflare/unenv-preset": "2.14.0",
|
||||
"blake3-wasm": "2.1.5",
|
||||
"esbuild": "0.27.0",
|
||||
"miniflare": "4.20260128.0",
|
||||
"esbuild": "0.27.3",
|
||||
"miniflare": "4.20260305.0",
|
||||
"path-to-regexp": "6.3.0",
|
||||
"unenv": "2.0.0-rc.24",
|
||||
"workerd": "1.20260128.0"
|
||||
"workerd": "1.20260305.0"
|
||||
},
|
||||
"bin": {
|
||||
"wrangler": "bin/wrangler.js",
|
||||
@@ -3175,7 +3174,7 @@
|
||||
"fsevents": "~2.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20260128.0"
|
||||
"@cloudflare/workers-types": "^4.20260305.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@cloudflare/workers-types": {
|
||||
|
||||
+2
-3
@@ -12,8 +12,7 @@
|
||||
"web:dev": "vite --config webapp/vite.config.ts",
|
||||
"web:build": "vite build --config webapp/vite.config.ts",
|
||||
"web:typecheck": "tsc -p webapp/tsconfig.json --noEmit",
|
||||
"deploymy": "wrangler deploy -c wrangler.my.toml",
|
||||
"deploy": "wrangler deploy"
|
||||
"deploy": "npm run web:build && wrangler deploy"
|
||||
},
|
||||
"keywords": [
|
||||
"bitwarden",
|
||||
@@ -42,7 +41,7 @@
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.3.1",
|
||||
"wrangler": "^4.61.1"
|
||||
"wrangler": "^4.69.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.90.21",
|
||||
|
||||
@@ -103,6 +103,14 @@
|
||||
// Max IDs per SQL batch when moving ciphers in bulk.
|
||||
// 批量移动密码项时每批 SQL 的最大 ID 数量。
|
||||
bulkMoveChunkSize: 200,
|
||||
// Max total items (folders + ciphers) allowed in a single import.
|
||||
// 单次导入允许的最大条目数(文件夹 + 密码项合计)。
|
||||
importItemLimit: 5000,
|
||||
},
|
||||
request: {
|
||||
// Hard body size limit for JSON API endpoints (bytes). File upload paths are exempt.
|
||||
// JSON 接口请求 body 大小上限(字节),文件上传接口除外。
|
||||
maxBodyBytes: 25 * 1024 * 1024,
|
||||
},
|
||||
compatibility: {
|
||||
// Single source of truth for /config.version and /api/version.
|
||||
|
||||
@@ -18,6 +18,32 @@ function looksLikeEncString(value: string): boolean {
|
||||
return parts.length >= 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate KDF parameters according to Bitwarden minimum requirements.
|
||||
* Returns an error message if invalid, or null if OK.
|
||||
*/
|
||||
function validateKdfParams(kdfType: number | undefined, kdfIterations: number | undefined, kdfMemory?: number | undefined, kdfParallelism?: number | undefined): string | null {
|
||||
const type = kdfType ?? 0;
|
||||
if (type === 0) {
|
||||
// PBKDF2-SHA256: minimum 100 000 iterations
|
||||
if (typeof kdfIterations === 'number' && kdfIterations < 100_000) {
|
||||
return 'PBKDF2 iterations must be at least 100000';
|
||||
}
|
||||
} else if (type === 1) {
|
||||
// Argon2id: iterations >= 2, memory >= 16 MiB, parallelism >= 1
|
||||
if (typeof kdfIterations === 'number' && kdfIterations < 2) {
|
||||
return 'Argon2id iterations must be at least 2';
|
||||
}
|
||||
if (typeof kdfMemory === 'number' && kdfMemory < 16) {
|
||||
return 'Argon2id memory must be at least 16 MiB';
|
||||
}
|
||||
if (typeof kdfParallelism === 'number' && kdfParallelism < 1) {
|
||||
return 'Argon2id parallelism must be at least 1';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function normalizeTotpSecret(input: string): string {
|
||||
return input.toUpperCase().replace(/[\s-]/g, '').replace(/=+$/g, '');
|
||||
}
|
||||
@@ -111,6 +137,9 @@ export async function handleRegister(request: Request, env: Env): Promise<Respon
|
||||
if (!email || !masterPasswordHash || !key) {
|
||||
return errorResponse('Email, masterPasswordHash, and key are required', 400);
|
||||
}
|
||||
if (!email.includes('@') || email.length < 3) {
|
||||
return errorResponse('Invalid email address', 400);
|
||||
}
|
||||
if (!privateKey || !publicKey) {
|
||||
return errorResponse('Private key and public key are required', 400);
|
||||
}
|
||||
@@ -121,6 +150,9 @@ export async function handleRegister(request: Request, env: Env): Promise<Respon
|
||||
return errorResponse('encryptedPrivateKey is not a valid encrypted string', 400);
|
||||
}
|
||||
|
||||
const kdfErr = validateKdfParams(body.kdf, body.kdfIterations, body.kdfMemory, body.kdfParallelism);
|
||||
if (kdfErr) return errorResponse(kdfErr, 400);
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const auth = new AuthService(env);
|
||||
const serverHash = await auth.hashPasswordServer(masterPasswordHash, email);
|
||||
@@ -338,6 +370,9 @@ export async function handleChangePassword(request: Request, env: Env, userId: s
|
||||
return errorResponse('new encryptedPrivateKey is not a valid encrypted string', 400);
|
||||
}
|
||||
|
||||
const kdfErr = validateKdfParams(body.kdf ?? user.kdfType, body.kdfIterations, body.kdfMemory, body.kdfParallelism);
|
||||
if (kdfErr) return errorResponse(kdfErr, 400);
|
||||
|
||||
user.masterPasswordHash = await auth.hashPasswordServer(body.newMasterPasswordHash, user.email);
|
||||
if (nextKey) user.key = nextKey;
|
||||
if (nextPrivateKey) user.privateKey = nextPrivateKey;
|
||||
@@ -350,6 +385,15 @@ export async function handleChangePassword(request: Request, env: Env, userId: s
|
||||
user.updatedAt = new Date().toISOString();
|
||||
await storage.saveUser(user);
|
||||
await storage.deleteRefreshTokensByUserId(user.id);
|
||||
await storage.createAuditLog({
|
||||
id: generateUUID(),
|
||||
actorUserId: user.id,
|
||||
action: 'user.password.change',
|
||||
targetType: 'user',
|
||||
targetId: user.id,
|
||||
metadata: JSON.stringify({ email: user.email }),
|
||||
createdAt: user.updatedAt,
|
||||
});
|
||||
|
||||
return new Response(null, { status: 200 });
|
||||
}
|
||||
|
||||
@@ -255,6 +255,28 @@ export async function handleAdminDeleteUser(
|
||||
return errorResponse('User not found', 404);
|
||||
}
|
||||
|
||||
// Clean up R2 files before DB cascade deletes the metadata rows.
|
||||
// 1. Attachment files (keyed by cipherId/attachmentId)
|
||||
const attachmentMap = await storage.getAttachmentsByUserId(target.id);
|
||||
for (const [cipherId, attachments] of attachmentMap) {
|
||||
for (const att of attachments) {
|
||||
await env.ATTACHMENTS.delete(`${cipherId}/${att.id}`);
|
||||
}
|
||||
}
|
||||
// 2. Send files (keyed by sends/sendId/fileId)
|
||||
const sends = await storage.getAllSends(target.id);
|
||||
for (const send of sends) {
|
||||
if (send.type === 1) { // SendType.File
|
||||
try {
|
||||
const parsed = JSON.parse(send.data) as Record<string, unknown>;
|
||||
const fileId = typeof parsed.id === 'string' ? parsed.id : null;
|
||||
if (fileId) {
|
||||
await env.ATTACHMENTS.delete(`sends/${send.id}/${fileId}`);
|
||||
}
|
||||
} catch { /* non-file send or bad data, skip */ }
|
||||
}
|
||||
}
|
||||
|
||||
await storage.deleteRefreshTokensByUserId(target.id);
|
||||
await storage.deleteUserById(target.id);
|
||||
await writeAuditLog(storage, actorUser.id, 'admin.user.delete', 'user', target.id, {
|
||||
|
||||
@@ -144,6 +144,12 @@ export async function handleGetCipher(request: Request, env: Env, userId: string
|
||||
return jsonResponse(cipherToResponse(cipher, attachments));
|
||||
}
|
||||
|
||||
async function verifyFolderOwnership(storage: StorageService, folderId: string | null | undefined, userId: string): Promise<boolean> {
|
||||
if (!folderId) return true;
|
||||
const folder = await storage.getFolder(folderId);
|
||||
return !!(folder && folder.userId === userId);
|
||||
}
|
||||
|
||||
// POST /api/ciphers
|
||||
export async function handleCreateCipher(request: Request, env: Env, userId: string): Promise<Response> {
|
||||
const storage = new StorageService(env.DB);
|
||||
@@ -178,6 +184,12 @@ export async function handleCreateCipher(request: Request, env: Env, userId: str
|
||||
const createFields = getAliasedProp(cipherData, ['fields', 'Fields']);
|
||||
cipher.fields = createFields.present ? (createFields.value ?? null) : (cipher.fields ?? null);
|
||||
|
||||
// Prevent referencing a folder owned by another user.
|
||||
if (cipher.folderId) {
|
||||
const folderOk = await verifyFolderOwnership(storage, cipher.folderId, userId);
|
||||
if (!folderOk) return errorResponse('Folder not found', 404);
|
||||
}
|
||||
|
||||
await storage.saveCipher(cipher);
|
||||
await storage.updateRevisionDate(userId);
|
||||
|
||||
@@ -232,6 +244,12 @@ export async function handleUpdateCipher(request: Request, env: Env, userId: str
|
||||
cipher.fields = null;
|
||||
}
|
||||
|
||||
// Prevent referencing a folder owned by another user.
|
||||
if (cipher.folderId) {
|
||||
const folderOk = await verifyFolderOwnership(storage, cipher.folderId, userId);
|
||||
if (!folderOk) return errorResponse('Folder not found', 404);
|
||||
}
|
||||
|
||||
await storage.saveCipher(cipher);
|
||||
await storage.updateRevisionDate(userId);
|
||||
|
||||
@@ -331,6 +349,10 @@ export async function handlePartialUpdateCipher(request: Request, env: Env, user
|
||||
}
|
||||
|
||||
if (body.folderId !== undefined) {
|
||||
if (body.folderId) {
|
||||
const folderOk = await verifyFolderOwnership(storage, body.folderId, userId);
|
||||
if (!folderOk) return errorResponse('Folder not found', 404);
|
||||
}
|
||||
cipher.folderId = body.folderId;
|
||||
}
|
||||
if (body.favorite !== undefined) {
|
||||
@@ -359,6 +381,11 @@ export async function handleBulkMoveCiphers(request: Request, env: Env, userId:
|
||||
return errorResponse('ids array is required', 400);
|
||||
}
|
||||
|
||||
if (body.folderId) {
|
||||
const folderOk = await verifyFolderOwnership(storage, body.folderId, userId);
|
||||
if (!folderOk) return errorResponse('Folder not found', 404);
|
||||
}
|
||||
|
||||
await storage.bulkMoveCiphers(body.ids, body.folderId || null, userId);
|
||||
|
||||
return new Response(null, { status: 204 });
|
||||
|
||||
@@ -391,8 +391,10 @@ export async function handlePrelogin(request: Request, env: Env): Promise<Respon
|
||||
// Return default KDF settings even if user doesn't exist (to prevent user enumeration)
|
||||
const kdfType = user?.kdfType ?? 0;
|
||||
const kdfIterations = user?.kdfIterations ?? LIMITS.auth.defaultKdfIterations;
|
||||
const kdfMemory = user?.kdfMemory;
|
||||
const kdfParallelism = user?.kdfParallelism;
|
||||
// Use ?? null so non-existent users return null (not undefined/omitted) for these fields,
|
||||
// matching the response shape of real PBKDF2 users and reducing enumeration signal.
|
||||
const kdfMemory = user?.kdfMemory ?? null;
|
||||
const kdfParallelism = user?.kdfParallelism ?? null;
|
||||
|
||||
return jsonResponse({
|
||||
kdf: kdfType,
|
||||
|
||||
@@ -102,6 +102,10 @@ export async function handleCiphersImport(request: Request, env: Env, userId: st
|
||||
const ciphers = importData.ciphers || [];
|
||||
const folderRelationships = importData.folderRelationships || [];
|
||||
|
||||
if (folders.length + ciphers.length > LIMITS.performance.importItemLimit) {
|
||||
return errorResponse(`Import exceeds maximum of ${LIMITS.performance.importItemLimit} items`, 400);
|
||||
}
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const batchChunkSize = LIMITS.performance.bulkMoveChunkSize;
|
||||
|
||||
|
||||
+16
-8
@@ -1022,9 +1022,11 @@ export async function handleAccessSend(request: Request, env: Env, accessId: str
|
||||
}
|
||||
|
||||
if (send.type === SendType.Text) {
|
||||
const updated = await storage.incrementSendAccessCount(send.id);
|
||||
if (!updated) {
|
||||
return errorResponse(SEND_INACCESSIBLE_MSG, 404);
|
||||
}
|
||||
send.accessCount += 1;
|
||||
send.updatedAt = new Date().toISOString();
|
||||
await storage.saveSend(send);
|
||||
await storage.updateRevisionDate(send.userId);
|
||||
}
|
||||
|
||||
@@ -1068,9 +1070,11 @@ export async function handleAccessSendFile(
|
||||
return validationErr;
|
||||
}
|
||||
|
||||
const updated = await storage.incrementSendAccessCount(send.id);
|
||||
if (!updated) {
|
||||
return errorResponse(SEND_INACCESSIBLE_MSG, 404);
|
||||
}
|
||||
send.accessCount += 1;
|
||||
send.updatedAt = new Date().toISOString();
|
||||
await storage.saveSend(send);
|
||||
await storage.updateRevisionDate(send.userId);
|
||||
|
||||
const token = await createSendFileDownloadToken(send.id, fileId, secret);
|
||||
@@ -1106,9 +1110,11 @@ export async function handleAccessSendV2(request: Request, env: Env): Promise<Re
|
||||
}
|
||||
|
||||
if (send.type === SendType.Text) {
|
||||
const updated = await storage.incrementSendAccessCount(send.id);
|
||||
if (!updated) {
|
||||
return errorResponse(SEND_INACCESSIBLE_MSG, 404);
|
||||
}
|
||||
send.accessCount += 1;
|
||||
send.updatedAt = new Date().toISOString();
|
||||
await storage.saveSend(send);
|
||||
await storage.updateRevisionDate(send.userId);
|
||||
}
|
||||
|
||||
@@ -1145,9 +1151,11 @@ export async function handleAccessSendFileV2(request: Request, env: Env, fileId:
|
||||
return errorResponse(SEND_INACCESSIBLE_MSG, 404);
|
||||
}
|
||||
|
||||
const updated = await storage.incrementSendAccessCount(send.id);
|
||||
if (!updated) {
|
||||
return errorResponse(SEND_INACCESSIBLE_MSG, 404);
|
||||
}
|
||||
send.accessCount += 1;
|
||||
send.updatedAt = new Date().toISOString();
|
||||
await storage.saveSend(send);
|
||||
await storage.updateRevisionDate(send.userId);
|
||||
|
||||
const downloadToken = await createSendFileDownloadToken(send.id, fileId, secret);
|
||||
|
||||
+4
-2
@@ -34,12 +34,14 @@ export default {
|
||||
void ctx;
|
||||
await ensureDatabaseInitialized(env);
|
||||
if (dbInitError) {
|
||||
// Log full error server-side, return generic message to client.
|
||||
console.error('DB init error (not forwarded to client):', dbInitError);
|
||||
const resp = jsonResponse(
|
||||
{
|
||||
error: 'Database not initialized',
|
||||
error_description: dbInitError,
|
||||
error_description: 'Database initialization failed. Check server logs for details.',
|
||||
ErrorModel: {
|
||||
Message: dbInitError,
|
||||
Message: 'Service temporarily unavailable',
|
||||
Object: 'error',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -240,6 +240,18 @@ export async function handleRequest(request: Request, env: Env): Promise<Respons
|
||||
// Route matching
|
||||
try {
|
||||
|
||||
// Reject oversized bodies before any path-specific parsing.
|
||||
// File upload paths enforce their own limits and are exempt here.
|
||||
const isFileUploadPath =
|
||||
/^\/api\/ciphers\/[a-f0-9-]+\/attachment\/[a-f0-9-]+$/i.test(path) ||
|
||||
/^\/api\/sends\/[a-f0-9-]+\/file\/[a-f0-9-]+$/i.test(path);
|
||||
if (!isFileUploadPath) {
|
||||
const contentLength = parseInt(request.headers.get('Content-Length') || '0', 10);
|
||||
if (contentLength > LIMITS.request.maxBodyBytes) {
|
||||
return errorResponse('Request body too large', 413);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup status
|
||||
if (path === '/setup/status' && method === 'GET') {
|
||||
return handleSetupStatus(request, env);
|
||||
@@ -328,6 +340,8 @@ export async function handleRequest(request: Request, env: Env): Promise<Respons
|
||||
|
||||
// Notifications hub (stub - no auth required, return 200 for connection)
|
||||
if (path.startsWith('/notifications/')) {
|
||||
const blocked = await enforcePublicRateLimit();
|
||||
if (blocked) return blocked;
|
||||
return new Response(null, { status: 200 });
|
||||
}
|
||||
|
||||
|
||||
+39
-5
@@ -425,7 +425,13 @@ export class StorageService {
|
||||
|
||||
async getCipher(id: string): Promise<Cipher | null> {
|
||||
const row = await this.db.prepare('SELECT data FROM ciphers WHERE id = ?').bind(id).first<{ data: string }>();
|
||||
return row?.data ? (JSON.parse(row.data) as Cipher) : null;
|
||||
if (!row?.data) return null;
|
||||
try {
|
||||
return JSON.parse(row.data) as Cipher;
|
||||
} catch {
|
||||
console.error('Corrupted cipher data, id:', id);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async saveCipher(cipher: Cipher): Promise<void> {
|
||||
@@ -460,7 +466,9 @@ export class StorageService {
|
||||
|
||||
async getAllCiphers(userId: string): Promise<Cipher[]> {
|
||||
const res = await this.db.prepare('SELECT data FROM ciphers WHERE user_id = ? ORDER BY updated_at DESC').bind(userId).all<{ data: string }>();
|
||||
return (res.results || []).map(r => JSON.parse(r.data) as Cipher);
|
||||
return (res.results || []).flatMap(r => {
|
||||
try { return [JSON.parse(r.data) as Cipher]; } catch { return []; }
|
||||
});
|
||||
}
|
||||
|
||||
async getCiphersPage(userId: string, includeDeleted: boolean, limit: number, offset: number): Promise<Cipher[]> {
|
||||
@@ -475,7 +483,9 @@ export class StorageService {
|
||||
)
|
||||
.bind(userId, limit, offset)
|
||||
.all<{ data: string }>();
|
||||
return (res.results || []).map(r => JSON.parse(r.data) as Cipher);
|
||||
return (res.results || []).flatMap(r => {
|
||||
try { return [JSON.parse(r.data) as Cipher]; } catch { return []; }
|
||||
});
|
||||
}
|
||||
|
||||
async getCiphersByIds(ids: string[], userId: string): Promise<Cipher[]> {
|
||||
@@ -484,7 +494,9 @@ export class StorageService {
|
||||
const placeholders = ids.map(() => '?').join(',');
|
||||
const stmt = this.db.prepare(`SELECT data FROM ciphers WHERE user_id = ? AND id IN (${placeholders})`);
|
||||
const res = await stmt.bind(userId, ...ids).all<{ data: string }>();
|
||||
return (res.results || []).map(r => JSON.parse(r.data) as Cipher);
|
||||
return (res.results || []).flatMap(r => {
|
||||
try { return [JSON.parse(r.data) as Cipher]; } catch { return []; }
|
||||
});
|
||||
}
|
||||
|
||||
async bulkMoveCiphers(ids: string[], folderId: string | null, userId: string): Promise<void> {
|
||||
@@ -555,7 +567,12 @@ export class StorageService {
|
||||
.all<{ data: string }>();
|
||||
|
||||
for (const row of (res.results || [])) {
|
||||
const cipher = JSON.parse(row.data) as Cipher;
|
||||
let cipher: Cipher;
|
||||
try {
|
||||
cipher = JSON.parse(row.data) as Cipher;
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
cipher.folderId = null;
|
||||
cipher.updatedAt = now;
|
||||
await this.saveCipher(cipher);
|
||||
@@ -857,6 +874,23 @@ export class StorageService {
|
||||
).run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increment access_count and update updated_at.
|
||||
* Returns true if the row was updated (send still available),
|
||||
* false if max_access_count has already been reached.
|
||||
*/
|
||||
async incrementSendAccessCount(sendId: string): Promise<boolean> {
|
||||
const now = new Date().toISOString();
|
||||
const result = await this.db
|
||||
.prepare(
|
||||
'UPDATE sends SET access_count = access_count + 1, updated_at = ? ' +
|
||||
'WHERE id = ? AND (max_access_count IS NULL OR access_count < max_access_count)'
|
||||
)
|
||||
.bind(now, sendId)
|
||||
.run();
|
||||
return (result.meta.changes ?? 0) > 0;
|
||||
}
|
||||
|
||||
async deleteSend(id: string, userId: string): Promise<void> {
|
||||
await this.db.prepare('DELETE FROM sends WHERE id = ? AND user_id = ?').bind(id, userId).run();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ const CORS_HEADERS = 'Content-Type, Authorization, Accept, Device-Type, Bitwarde
|
||||
|
||||
function isTrustedClientOrigin(origin: string): boolean {
|
||||
// Official browser extension / desktop-webview common origins.
|
||||
if (origin === 'null') return true;
|
||||
if (origin.startsWith('chrome-extension://')) return true;
|
||||
if (origin.startsWith('moz-extension://')) return true;
|
||||
if (origin.startsWith('safari-web-extension://')) return true;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://icons.bitwarden.net; connect-src 'self'; font-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self';" />
|
||||
<link rel="icon" type="image/png" href="/favicon.ico" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<title>NodeWarden</title>
|
||||
|
||||
@@ -56,7 +56,8 @@ export default function SettingsPage(props: SettingsPageProps) {
|
||||
|
||||
async function enableTotp(): Promise<void> {
|
||||
await props.onEnableTotp(secret, token);
|
||||
localStorage.setItem(totpSecretStorageKey, secret);
|
||||
// Secret is now stored on the server; remove plaintext copy from localStorage.
|
||||
localStorage.removeItem(totpSecretStorageKey);
|
||||
setTotpLocked(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -993,9 +993,8 @@ async function buildPublicSendAccessPayload(password?: string, keyPart?: string
|
||||
const payload: Record<string, unknown> = {};
|
||||
const plainPassword = String(password || '').trim();
|
||||
if (!plainPassword) return payload;
|
||||
payload.password = plainPassword;
|
||||
|
||||
// Official clients send a PBKDF2 hash bound to send key material.
|
||||
// Only send the PBKDF2 hash bound to the send key material — never send plaintext password.
|
||||
if (keyPart) {
|
||||
try {
|
||||
const sendKeyMaterial = base64UrlToBytes(keyPart);
|
||||
@@ -1004,7 +1003,7 @@ async function buildPublicSendAccessPayload(password?: string, keyPart?: string
|
||||
payload.password_hash_b64 = passwordHashB64;
|
||||
payload.passwordHashB64 = passwordHashB64;
|
||||
} catch {
|
||||
// Fallback to plain password for legacy compatibility.
|
||||
// Key material invalid; cannot compute hash — server will reject as unauthorized.
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
|
||||
Reference in New Issue
Block a user