diff --git a/README.md b/README.md index 7471ad3..71a96d6 100644 --- a/README.md +++ b/README.md @@ -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 ``` --- diff --git a/README_EN.md b/README_EN.md index 4baef5f..63989f7 100644 --- a/README_EN.md +++ b/README_EN.md @@ -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 ``` --- diff --git a/package-lock.json b/package-lock.json index f4b93c5..883a96b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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": { diff --git a/package.json b/package.json index 0da4e35..60d1ab1 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/config/limits.ts b/src/config/limits.ts index ae26aa1..58c78b4 100644 --- a/src/config/limits.ts +++ b/src/config/limits.ts @@ -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. diff --git a/src/handlers/accounts.ts b/src/handlers/accounts.ts index 993a1ca..b756076 100644 --- a/src/handlers/accounts.ts +++ b/src/handlers/accounts.ts @@ -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; + 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, { diff --git a/src/handlers/ciphers.ts b/src/handlers/ciphers.ts index 868f619..992c836 100644 --- a/src/handlers/ciphers.ts +++ b/src/handlers/ciphers.ts @@ -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 { + 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 { 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 }); diff --git a/src/handlers/identity.ts b/src/handlers/identity.ts index 3c30ca5..edccfa6 100644 --- a/src/handlers/identity.ts +++ b/src/handlers/identity.ts @@ -391,8 +391,10 @@ export async function handlePrelogin(request: Request, env: Env): Promise LIMITS.performance.importItemLimit) { + return errorResponse(`Import exceeds maximum of ${LIMITS.performance.importItemLimit} items`, 400); + } + const now = new Date().toISOString(); const batchChunkSize = LIMITS.performance.bulkMoveChunkSize; diff --git a/src/handlers/sends.ts b/src/handlers/sends.ts index 715303d..58b0988 100644 --- a/src/handlers/sends.ts +++ b/src/handlers/sends.ts @@ -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 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 { 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 { @@ -460,7 +466,9 @@ export class StorageService { async getAllCiphers(userId: string): Promise { 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 { @@ -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 { @@ -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 { @@ -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 { + 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 { await this.db.prepare('DELETE FROM sends WHERE id = ? AND user_id = ?').bind(id, userId).run(); } diff --git a/src/utils/response.ts b/src/utils/response.ts index c005789..a9bb24c 100644 --- a/src/utils/response.ts +++ b/src/utils/response.ts @@ -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; diff --git a/webapp/index.html b/webapp/index.html index d5af8d7..dfe3254 100644 --- a/webapp/index.html +++ b/webapp/index.html @@ -3,6 +3,7 @@ + NodeWarden diff --git a/webapp/src/components/SettingsPage.tsx b/webapp/src/components/SettingsPage.tsx index 02ab725..77ebc73 100644 --- a/webapp/src/components/SettingsPage.tsx +++ b/webapp/src/components/SettingsPage.tsx @@ -56,7 +56,8 @@ export default function SettingsPage(props: SettingsPageProps) { async function enableTotp(): Promise { 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); } diff --git a/webapp/src/lib/api.ts b/webapp/src/lib/api.ts index 7f19969..b11c8f9 100644 --- a/webapp/src/lib/api.ts +++ b/webapp/src/lib/api.ts @@ -993,9 +993,8 @@ async function buildPublicSendAccessPayload(password?: string, keyPart?: string const payload: Record = {}; 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;