From 261bca67bc7e0a4c188cb8d57522998ae9fdb735 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 14:59:19 +0200 Subject: [PATCH 01/53] chore(deps): update all non-major dependencies (#5929) --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 1248 +++++++++++++++++++-------------------- go.mod | 10 +- go.sum | 20 +- 4 files changed, 626 insertions(+), 654 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index eb96f40f3f..8bea2d8a6d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -74,5 +74,5 @@ "vitest": "^4.1.0", "vue-tsc": "^3.1.3" }, - "packageManager": "pnpm@10.33.3+sha512.a19744364a7e248b92657a4ca5973f9354d21caf982579674b1c539f32c7420c47138ad8b1254df07aba9bc782d9b3029e3db34d5dbff974326eb74dac8ff489" + "packageManager": "pnpm@10.33.4+sha512.1c67b3b359b2d408119ba1ed289f34b8fc3c6873412bec6fd264fbdc82489e510fcbecb9ce9d22dae7f3b76269d8441046014bdca53b9979cd7a561ad631b800" } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 54b3ab55d3..a993e7e632 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -10,16 +10,16 @@ importers: dependencies: '@chenfengyuan/vue-number-input': specifier: ^2.0.1 - version: 2.0.1(vue@3.5.33(typescript@5.9.3)) + version: 2.0.1(vue@3.5.34(typescript@5.9.3)) '@vueuse/core': specifier: ^14.0.0 - version: 14.3.0(vue@3.5.33(typescript@5.9.3)) + version: 14.3.0(vue@3.5.34(typescript@5.9.3)) '@vueuse/integrations': specifier: ^14.0.0 - version: 14.3.0(focus-trap@8.0.0)(jwt-decode@4.0.0)(vue@3.5.33(typescript@5.9.3)) + version: 14.3.0(focus-trap@8.0.0)(jwt-decode@4.0.0)(vue@3.5.34(typescript@5.9.3)) ace-builds: specifier: ^1.43.2 - version: 1.43.6 + version: 1.44.0 csv-parse: specifier: ^6.1.0 version: 6.2.1 @@ -28,7 +28,7 @@ importers: version: 1.11.20 dompurify: specifier: ^3.2.6 - version: 3.4.2 + version: 3.4.4 epubjs: specifier: ^0.3.93 version: 0.3.93 @@ -58,13 +58,13 @@ importers: version: 8.0.1 pinia: specifier: ^3.0.4 - version: 3.0.4(typescript@5.9.3)(vue@3.5.33(typescript@5.9.3)) + version: 3.0.4(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)) pretty-bytes: specifier: ^7.1.0 version: 7.1.0 qrcode.vue: specifier: ^3.6.0 - version: 3.9.0(vue@3.5.33(typescript@5.9.3)) + version: 3.9.1(vue@3.5.34(typescript@5.9.3)) tus-js-client: specifier: ^4.3.1 version: 4.3.1 @@ -82,10 +82,10 @@ importers: version: 1.2.5(video.js@8.23.7) vue: specifier: ^3.5.17 - version: 3.5.33(typescript@5.9.3) + version: 3.5.34(typescript@5.9.3) vue-i18n: specifier: ^11.1.10 - version: 11.4.0(vue@3.5.33(typescript@5.9.3)) + version: 11.4.2(vue@3.5.34(typescript@5.9.3)) vue-lazyload: specifier: ^3.0.0 version: 3.0.0 @@ -94,14 +94,14 @@ importers: version: 1.3.4 vue-router: specifier: ^5.0.0 - version: 5.0.6(@vue/compiler-sfc@3.5.33)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.33(typescript@5.9.3)))(vue@3.5.33(typescript@5.9.3)) + version: 5.0.7(@vue/compiler-sfc@3.5.34)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)))(vue@3.5.34(typescript@5.9.3)) vue-toastification: specifier: ^2.0.0-rc.5 - version: 2.0.0-rc.5(vue@3.5.33(typescript@5.9.3)) + version: 2.0.0-rc.5(vue@3.5.34(typescript@5.9.3)) devDependencies: '@intlify/unplugin-vue-i18n': specifier: ^11.0.1 - version: 11.1.2(@vue/compiler-dom@3.5.33)(eslint@10.3.0)(rollup@4.57.1)(typescript@5.9.3)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3))(vue-i18n@11.4.0(vue@3.5.33(typescript@5.9.3)))(vue@3.5.33(typescript@5.9.3)) + version: 11.2.3(@vue/compiler-dom@3.5.34)(eslint@10.4.0)(rollup@4.57.1)(typescript@5.9.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0))(vue-i18n@11.4.2(vue@3.5.34(typescript@5.9.3)))(vue@3.5.34(typescript@5.9.3)) '@tsconfig/node24': specifier: ^24.0.2 version: 24.0.4 @@ -110,40 +110,40 @@ importers: version: 4.17.12 '@types/node': specifier: ^24.10.1 - version: 24.12.2 + version: 24.12.4 '@typescript-eslint/eslint-plugin': specifier: ^8.37.0 - version: 8.59.2(@typescript-eslint/parser@8.56.0(eslint@10.3.0)(typescript@5.9.3))(eslint@10.3.0)(typescript@5.9.3) + version: 8.59.3(@typescript-eslint/parser@8.56.0(eslint@10.4.0)(typescript@5.9.3))(eslint@10.4.0)(typescript@5.9.3) '@vitejs/plugin-legacy': specifier: ^8.0.0 - version: 8.0.1(terser@5.46.2)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3)) + version: 8.0.2(terser@5.47.1)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0)) '@vitejs/plugin-vue': specifier: ^6.0.1 - version: 6.0.6(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3))(vue@3.5.33(typescript@5.9.3)) + version: 6.0.7(vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0))(vue@3.5.34(typescript@5.9.3)) '@vue/eslint-config-prettier': specifier: ^10.2.0 - version: 10.2.0(eslint@10.3.0)(prettier@3.8.3) + version: 10.2.0(eslint@10.4.0)(prettier@3.8.3) '@vue/eslint-config-typescript': specifier: ^14.6.0 - version: 14.7.0(eslint-plugin-vue@10.9.0(@typescript-eslint/parser@8.56.0(eslint@10.3.0)(typescript@5.9.3))(eslint@10.3.0)(vue-eslint-parser@10.4.0(eslint@10.3.0)))(eslint@10.3.0)(typescript@5.9.3) + version: 14.7.0(eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.56.0(eslint@10.4.0)(typescript@5.9.3))(eslint@10.4.0)(vue-eslint-parser@10.4.0(eslint@10.4.0)))(eslint@10.4.0)(typescript@5.9.3) '@vue/tsconfig': specifier: ^0.9.0 - version: 0.9.1(typescript@5.9.3)(vue@3.5.33(typescript@5.9.3)) + version: 0.9.1(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)) autoprefixer: specifier: ^10.4.21 version: 10.5.0(postcss@8.5.14) eslint: specifier: ^10.0.0 - version: 10.3.0 + version: 10.4.0 eslint-config-prettier: specifier: ^10.1.5 - version: 10.1.8(eslint@10.3.0) + version: 10.1.8(eslint@10.4.0) eslint-plugin-prettier: specifier: ^5.5.1 - version: 5.5.5(eslint-config-prettier@10.1.8(eslint@10.3.0))(eslint@10.3.0)(prettier@3.8.3) + version: 5.5.5(eslint-config-prettier@10.1.8(eslint@10.4.0))(eslint@10.4.0)(prettier@3.8.3) eslint-plugin-vue: specifier: ^10.5.1 - version: 10.9.0(@typescript-eslint/parser@8.56.0(eslint@10.3.0)(typescript@5.9.3))(eslint@10.3.0)(vue-eslint-parser@10.4.0(eslint@10.3.0)) + version: 10.9.1(@typescript-eslint/parser@8.56.0(eslint@10.4.0)(typescript@5.9.3))(eslint@10.4.0)(vue-eslint-parser@10.4.0(eslint@10.4.0)) postcss: specifier: ^8.5.6 version: 8.5.14 @@ -152,22 +152,22 @@ importers: version: 3.8.3 terser: specifier: ^5.43.1 - version: 5.46.2 + version: 5.47.1 typescript: specifier: ^5.9.3 version: 5.9.3 vite: specifier: ^8.0.0 - version: 8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3) + version: 8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0) vite-plugin-compression2: specifier: ^2.3.1 version: 2.5.3(rollup@4.57.1) vitest: specifier: ^4.1.0 - version: 4.1.5(@types/node@24.12.2)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3)) + version: 4.1.6(@types/node@24.12.4)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0)) vue-tsc: specifier: ^3.1.3 - version: 3.2.8(typescript@5.9.3) + version: 3.2.9(typescript@5.9.3) packages: @@ -175,8 +175,8 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.0': - resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + '@babel/compat-data@7.29.3': + resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} engines: {node: '>=6.9.0'} '@babel/core@7.29.0': @@ -187,6 +187,10 @@ packages: resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} + '@babel/generator@8.0.0-rc.5': + resolution: {integrity: sha512-nFZPWz3FHIS7y6rMIVoa/WBwjdutfIaRJIBQjzn+t3RnecZoRNlGmGcyR2wb0T/IgSd50Kz/6dG8/LvMCRunjg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} @@ -195,8 +199,8 @@ packages: resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.28.6': - resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} + '@babel/helper-create-class-features-plugin@7.29.3': + resolution: {integrity: sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -258,10 +262,18 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@8.0.0-rc.5': + resolution: {integrity: sha512-sN7R8rBvDurfaziNfDEIjIntlazmlkCDGO4SNl2RJ3wRCn+QxspLV7hzYAE8WWVd2joVuT8sUxeePdLp2idI1A==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@8.0.0-rc.5': + resolution: {integrity: sha512-ehJDxHvtbZ85RtX/L2fi0h9AGsBNqB5Euv1EB8RMAvGYvD+2X+QbpzzOpbklnNXO+WSZJNOaetw2BBj27xsWVg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -274,11 +286,16 @@ packages: resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.2': - resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@8.0.0-rc.5': + resolution: {integrity: sha512-/Mfg83rK3+jsRbl4Vbd0jqxc6M1A1/WNFtgrowRM1unEsD3XcNnrBdMM0JWakd0/RN9lseQKwPduW1TiEwKOlQ==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} @@ -297,6 +314,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.3': + resolution: {integrity: sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} engines: {node: '>=6.9.0'} @@ -483,8 +506,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.29.0': - resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==} + '@babel/plugin-transform-modules-systemjs@7.29.4': + resolution: {integrity: sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -639,8 +662,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.29.2': - resolution: {integrity: sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==} + '@babel/preset-env@7.29.5': + resolution: {integrity: sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -666,6 +689,10 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@babel/types@8.0.0-rc.5': + resolution: {integrity: sha512-JeSVu/m8x/zpp4CLjYHVNXuhEyOkhPXuxM8YOXjh6L4LlvQNKuUNOTo5KdBuKAcTDHw8DquToTaEkhsBqPXOaA==} + engines: {node: ^22.18.0 || >=24.11.0} + '@chenfengyuan/vue-number-input@2.0.1': resolution: {integrity: sha512-/jqmfmFulFOGlozts0Sf2GCESMRYVTfZZSz2Tf4n9O5DKjqMi5B/MfRzm5H5A57WuG3L80yXFWFN+XeACKaIhQ==} peerDependencies: @@ -1006,8 +1033,8 @@ packages: resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/config-helpers@0.5.5': - resolution: {integrity: sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==} + '@eslint/config-helpers@0.6.0': + resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/core@1.2.1': @@ -1042,9 +1069,9 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@intlify/bundle-utils@11.1.2': - resolution: {integrity: sha512-/Cd1MKb7L0SFnIvyhZO0YF4W+jSIY4BQ2PgiT0jP+tc1PO/W2mAOOx4/GecjA21oPhtSxyZd13vFAZnGGeZYVQ==} - engines: {node: '>= 20'} + '@intlify/bundle-utils@11.2.3': + resolution: {integrity: sha512-9mrJyUJGPFJCIFGthvIFT58CknG701z9D0VRtLBtat3teo0fisP3Q6bo/t9YHnljBTEZ42hYm1ukn16LfLkRRg==} + engines: {node: '>= 22.13'} peerDependencies: petite-vue-i18n: '*' vue-i18n: '*' @@ -1054,25 +1081,25 @@ packages: vue-i18n: optional: true - '@intlify/core-base@11.4.0': - resolution: {integrity: sha512-nlxFOnmjJgVkL1PsuSMagyh3qIHTwc2KlO2R3qQQV1ydrcwh1XpM7opWUGqvGaLlktttopDzbLBpr/k5tvbNmA==} + '@intlify/core-base@11.4.2': + resolution: {integrity: sha512-7fpuCcVmeLv2T9qHsARqGvh8xt+sV2fH+Q+gMHFwB/rPXzo85DpbJFKn7dBH1L5p0c2cSh2DW+2h/64EKrISmA==} engines: {node: '>= 16'} - '@intlify/devtools-types@11.4.0': - resolution: {integrity: sha512-LtQ04kG8/2Nv6AbuINpkjODuhKHdd+MGLlXKW3I0GTCeDsDIBZUot82nnyK7D6+qersF08FqSvoN/eGPcL3c7Q==} + '@intlify/devtools-types@11.4.2': + resolution: {integrity: sha512-3u8EN1kB6EMSi96KXs5k7a8y2X2g4+h3X6iwVZU47cP4n+mTuq//WMjG588BzSp/2XQ/dTXo2BLUXX+XS+PNfA==} engines: {node: '>= 16'} - '@intlify/message-compiler@11.4.0': - resolution: {integrity: sha512-v455gVZqMb0er63Wd/akX8DXTnwSubgrgQaRigLB60V3xpnq3B99oPvGXW+N4G/5QFt8Ls84FJ8qHJUVnRCs1A==} + '@intlify/message-compiler@11.4.2': + resolution: {integrity: sha512-a6CDSGSMTGrg0BjD97x8TBYPf7qQMDlZipJ6UDfv/pd4OIym8TMlHu3MsH0bTNnRdAG2D6EFEykIgiQPqvtTkA==} engines: {node: '>= 16'} - '@intlify/shared@11.4.0': - resolution: {integrity: sha512-r9qUeLeO0TMZmUZ+mXS6IGQ6xwzZJaVMK6j4CdoA3eQP8xp3JtCfwkZ30gB4+knlN40pmBdDXgx85SWhMCzHng==} + '@intlify/shared@11.4.2': + resolution: {integrity: sha512-NzpHbguRCsOHDwxmlBa9qu/imc+/QWgsYUaK6FZeNC0wK8QfAbhqrktEp/haVzxU1aikH8IX4ytD+mfFEMi/9A==} engines: {node: '>= 16'} - '@intlify/unplugin-vue-i18n@11.1.2': - resolution: {integrity: sha512-3RUsT7ss+dzl12bu0MW6sWdy8fZ2rsysH+4fZnMF+ANK5UQ2nhGSw1795YoHtH0rZTDI198PdfodZfyrdt4KYw==} - engines: {node: '>= 20'} + '@intlify/unplugin-vue-i18n@11.2.3': + resolution: {integrity: sha512-fbPHjOVAkxrPnbhAs6PTNJlfLOJj35ZqYh8CZ9OpeKZiZoulA9lkvrWYP3kfsZ5K/CG9jIHXxpb1/mf5n/mBYA==} + engines: {node: '>= 22.13'} peerDependencies: petite-vue-i18n: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1141,113 +1168,110 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@oxc-project/types@0.127.0': - resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} + '@oxc-project/types@0.130.0': + resolution: {integrity: sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==} '@pkgr/core@0.2.9': resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@rolldown/binding-android-arm64@1.0.0-rc.17': - resolution: {integrity: sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==} + '@rolldown/binding-android-arm64@1.0.1': + resolution: {integrity: sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-rc.17': - resolution: {integrity: sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==} + '@rolldown/binding-darwin-arm64@1.0.1': + resolution: {integrity: sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-rc.17': - resolution: {integrity: sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==} + '@rolldown/binding-darwin-x64@1.0.1': + resolution: {integrity: sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-rc.17': - resolution: {integrity: sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==} + '@rolldown/binding-freebsd-x64@1.0.1': + resolution: {integrity: sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': - resolution: {integrity: sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.1': + resolution: {integrity: sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==} + '@rolldown/binding-linux-arm64-gnu@1.0.1': + resolution: {integrity: sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': - resolution: {integrity: sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==} + '@rolldown/binding-linux-arm64-musl@1.0.1': + resolution: {integrity: sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==} + '@rolldown/binding-linux-ppc64-gnu@1.0.1': + resolution: {integrity: sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==} + '@rolldown/binding-linux-s390x-gnu@1.0.1': + resolution: {integrity: sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==} + '@rolldown/binding-linux-x64-gnu@1.0.1': + resolution: {integrity: sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': - resolution: {integrity: sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==} + '@rolldown/binding-linux-x64-musl@1.0.1': + resolution: {integrity: sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': - resolution: {integrity: sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==} + '@rolldown/binding-openharmony-arm64@1.0.1': + resolution: {integrity: sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': - resolution: {integrity: sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==} + '@rolldown/binding-wasm32-wasi@1.0.1': + resolution: {integrity: sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': - resolution: {integrity: sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==} + '@rolldown/binding-win32-arm64-msvc@1.0.1': + resolution: {integrity: sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': - resolution: {integrity: sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==} + '@rolldown/binding-win32-x64-msvc@1.0.1': + resolution: {integrity: sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-rc.13': - resolution: {integrity: sha512-3ngTAv6F/Py35BsYbeeLeecvhMKdsKm4AoOETVhAA+Qc8nrA2I0kF7oa93mE9qnIurngOSpMnQ0x2nQY2FPviA==} - - '@rolldown/pluginutils@1.0.0-rc.17': - resolution: {integrity: sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==} + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} @@ -1402,8 +1426,8 @@ packages: '@tsconfig/node24@24.0.4': resolution: {integrity: sha512-2A933l5P5oCbv6qSxHs7ckKwobs8BDAe9SJ/Xr2Hy+nDlwmLE1GhFh/g/vXGRZWgxBg9nX/5piDtHR9Dkw/XuA==} - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -1417,6 +1441,12 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1430,8 +1460,8 @@ packages: '@types/lodash@4.17.23': resolution: {integrity: sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==} - '@types/node@24.12.2': - resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==} + '@types/node@24.12.4': + resolution: {integrity: sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==} '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -1447,11 +1477,11 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/eslint-plugin@8.59.2': - resolution: {integrity: sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==} + '@typescript-eslint/eslint-plugin@8.59.3': + resolution: {integrity: sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.59.2 + '@typescript-eslint/parser': ^8.59.3 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' @@ -1468,14 +1498,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.59.1': - resolution: {integrity: sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.1.0' - - '@typescript-eslint/project-service@8.59.2': - resolution: {integrity: sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==} + '@typescript-eslint/project-service@8.59.3': + resolution: {integrity: sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1484,12 +1508,8 @@ packages: resolution: {integrity: sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.59.1': - resolution: {integrity: sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/scope-manager@8.59.2': - resolution: {integrity: sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==} + '@typescript-eslint/scope-manager@8.59.3': + resolution: {integrity: sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.56.0': @@ -1498,14 +1518,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.59.1': - resolution: {integrity: sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.1.0' - - '@typescript-eslint/tsconfig-utils@8.59.2': - resolution: {integrity: sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==} + '@typescript-eslint/tsconfig-utils@8.59.3': + resolution: {integrity: sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1517,8 +1531,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.59.2': - resolution: {integrity: sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==} + '@typescript-eslint/type-utils@8.59.3': + resolution: {integrity: sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -1528,12 +1542,8 @@ packages: resolution: {integrity: sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.59.1': - resolution: {integrity: sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/types@8.59.2': - resolution: {integrity: sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==} + '@typescript-eslint/types@8.59.3': + resolution: {integrity: sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.56.0': @@ -1542,14 +1552,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.59.1': - resolution: {integrity: sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.1.0' - - '@typescript-eslint/typescript-estree@8.59.2': - resolution: {integrity: sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==} + '@typescript-eslint/typescript-estree@8.59.3': + resolution: {integrity: sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1561,8 +1565,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.59.2': - resolution: {integrity: sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==} + '@typescript-eslint/utils@8.59.3': + resolution: {integrity: sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -1572,12 +1576,8 @@ packages: resolution: {integrity: sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.59.1': - resolution: {integrity: sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/visitor-keys@8.59.2': - resolution: {integrity: sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==} + '@typescript-eslint/visitor-keys@8.59.3': + resolution: {integrity: sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@videojs/http-streaming@3.17.4': @@ -1593,25 +1593,25 @@ packages: '@videojs/xhr@2.7.0': resolution: {integrity: sha512-giab+EVRanChIupZK7gXjHy90y3nncA2phIOyG3Ne5fvpiMJzvqYwiTOnEVW2S4CoYcuKJkomat7bMXA/UoUZQ==} - '@vitejs/plugin-legacy@8.0.1': - resolution: {integrity: sha512-8zeDeuNPqXd49rIVgFgluQYB8vQICHR7l+W2I3CxYK4gTjTorajVr0wLvSjALIwEwLRxBn68EgNVyGP4j6hP7w==} + '@vitejs/plugin-legacy@8.0.2': + resolution: {integrity: sha512-+n6znc/hTsuD2v/qWX3nfR6UFWFKwpL+XS+SPyiPuEwAvR24iRvLkJQDh18u0XrHPEwuwxPmw0VopvlmLSg66Q==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: terser: ^5.16.0 vite: ^8.0.0 - '@vitejs/plugin-vue@6.0.6': - resolution: {integrity: sha512-u9HHgfrq3AjXlysn0eINFnWQOJQLO9WN6VprZ8FXl7A2bYisv3Hui9Ij+7QZ41F/WYWarHjwBbXtD7dKg3uxbg==} + '@vitejs/plugin-vue@6.0.7': + resolution: {integrity: sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 vue: ^3.2.25 - '@vitest/expect@4.1.5': - resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==} + '@vitest/expect@4.1.6': + resolution: {integrity: sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==} - '@vitest/mocker@4.1.5': - resolution: {integrity: sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==} + '@vitest/mocker@4.1.6': + resolution: {integrity: sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1621,20 +1621,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.5': - resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==} + '@vitest/pretty-format@4.1.6': + resolution: {integrity: sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==} - '@vitest/runner@4.1.5': - resolution: {integrity: sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==} + '@vitest/runner@4.1.6': + resolution: {integrity: sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==} - '@vitest/snapshot@4.1.5': - resolution: {integrity: sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==} + '@vitest/snapshot@4.1.6': + resolution: {integrity: sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==} - '@vitest/spy@4.1.5': - resolution: {integrity: sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==} + '@vitest/spy@4.1.6': + resolution: {integrity: sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==} - '@vitest/utils@4.1.5': - resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} + '@vitest/utils@4.1.6': + resolution: {integrity: sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==} '@volar/language-core@2.4.28': resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} @@ -1654,17 +1654,17 @@ packages: vue: optional: true - '@vue/compiler-core@3.5.33': - resolution: {integrity: sha512-3PZLQwFw4Za3TC8t0FvTy3wI16Kt+pmwcgNZca4Pj9iWL2E72a/gZlpBtAJvEdDMdCxdG/qq0C7PN0bsJuv0Rw==} + '@vue/compiler-core@3.5.34': + resolution: {integrity: sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw==} - '@vue/compiler-dom@3.5.33': - resolution: {integrity: sha512-PXq0yrfCLzzL07rbXO4awtXY1Z06LG2eu6Adg3RJFa/j3Cii217XxxLXG22N330gw7GmALCY0Z8RgXEviwgpjA==} + '@vue/compiler-dom@3.5.34': + resolution: {integrity: sha512-EbF/T++k0e2MMZlJsBhzK8Sgwt0HcIPOhzn1CTB/lv6sQcyk+OWf8YeiLxZp3ro7MbbLcAfAJ6sEvjFWuNgUCw==} - '@vue/compiler-sfc@3.5.33': - resolution: {integrity: sha512-UTUvRO9cY+rROrx/pvN9P5Z7FgA6QGfokUCfhQE4EnmUj3rVnK+CHI0LsEO1pg+I7//iRYMUfcNcCPe7tg0CoA==} + '@vue/compiler-sfc@3.5.34': + resolution: {integrity: sha512-D/ihr6uZeIt6r+pVZf46RWT1fAsLFMbUP7k8G1VkiiWexriED9GrX3echHd4Abbt17zjlfiFJ8z7a3BxZOPNjg==} - '@vue/compiler-ssr@3.5.33': - resolution: {integrity: sha512-IErjYdnj1qIupG5xxiVIYiiRvDhGWV4zuh/RCrwfYpuL+HWQzeU6lCk/nF9r7olWMnjKxCAkOctT2qFWFkzb1A==} + '@vue/compiler-ssr@3.5.34': + resolution: {integrity: sha512-cDtTHKibkThKGHH1SP+WdccquNRYQDFH6rRjQCqT9G2ltFAfoR5pUftpab/z+aM5mW9HLLVQW7hfKKQe/1GBeQ==} '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} @@ -1672,20 +1672,20 @@ packages: '@vue/devtools-api@7.7.9': resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} - '@vue/devtools-api@8.1.1': - resolution: {integrity: sha512-bsDMJ07b3GN1puVwJb/fyFnj/U2imyswK5UQVLZwVl7O05jDrt6BHxeG5XffmOOdasOj/bOmIjxJvGPxU7pcqw==} + '@vue/devtools-api@8.1.2': + resolution: {integrity: sha512-vA0O112YqyDuNA1s7Yb2gCgToQ/OxOWiFDO5ThLCcDy0ldHnSd1dUTaSYhOldbqoNgumE4dxtGAoAaSUKUD1Zg==} '@vue/devtools-kit@7.7.9': resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} - '@vue/devtools-kit@8.1.1': - resolution: {integrity: sha512-gVBaBv++i+adg4JpH71k9ppl4soyR7Y2McEqO5YNgv0BI1kMZ7BDX5gnwkZ5COYgiCyhejZG+yGNrBAjj6Coqg==} + '@vue/devtools-kit@8.1.2': + resolution: {integrity: sha512-f75/upc+GCyjXErpgPGz4582ujS0L/adAltGy+tqXMGUJpgAcfGr6CxnnhpZY8BHuMYt6KpbF8uaFrrQG66rGQ==} '@vue/devtools-shared@7.7.9': resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} - '@vue/devtools-shared@8.1.1': - resolution: {integrity: sha512-+h4ttmJYl/txpxHKaoZcaKpC+pvckgLzIDiSQlaQ7kKthKh8KuwoLW2D8hPJEnqKzXOvu15UHEoGyngAXCz0EQ==} + '@vue/devtools-shared@8.1.2': + resolution: {integrity: sha512-X9RyVFYAdkBe4IUf5v48TxBF/6QPmF8CmWrDAjXzfUHrgQ/HGfTC1A6TqgXqZ03ye66l3AD51BAGD69IvKM9sw==} '@vue/eslint-config-prettier@10.2.0': resolution: {integrity: sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==} @@ -1704,25 +1704,25 @@ packages: typescript: optional: true - '@vue/language-core@3.2.8': - resolution: {integrity: sha512-9OiSPQFiAAWNVnXb0d2dcTmcKnFQamhuNES6ayyISrb/mwPWVgoGdAqSfCWqKhQpa3D5gDTcYD+w7ObiheZ81g==} + '@vue/language-core@3.2.9': + resolution: {integrity: sha512-ie0ojt/0fU/GfIogh+zgHbaYRPlt9S+cLOxcWwF7nTSFh897BVgnFKL2byT4kpp1mlqYWZ2psGwSniyE2xsxYw==} - '@vue/reactivity@3.5.33': - resolution: {integrity: sha512-p8UfIqyIhb0rYGlSgSBV+lPhF2iUSBcRy7enhTmPqKWadHy9kcOFYF1AejYBP9P+avnd3OBbD49DU4pLWX/94A==} + '@vue/reactivity@3.5.34': + resolution: {integrity: sha512-y9XDjCEuBp+98k+UL5dbYkh57AHU4o6cxZedOPXw3bmrZZYLQsVHguGurq7hVrPCSrQtrnz1f9dssyFr+dMXfQ==} - '@vue/runtime-core@3.5.33': - resolution: {integrity: sha512-UpFF45RI9//a7rvq7RdOQblb4tup7hHG9QsmIrxkFQLzQ7R8/iNQ5LE15NhLZ1/WcHMU2b47u6P33CPUelHyIQ==} + '@vue/runtime-core@3.5.34': + resolution: {integrity: sha512-mKeBYvu8tcMSLhypAHBmriUFfWXKTCF/23Z4jiCoYK3UtWepkliViNLuR90V9XOyD62mUxs9p1jsrpK3CCGIzw==} - '@vue/runtime-dom@3.5.33': - resolution: {integrity: sha512-IOxMsAOwquhfITgmOgaPYl7/j8gKUxUFoflRc+u4LxyD3+783xne8vNta1PONVCvCV9A0w7hkyEepINDqfO0tw==} + '@vue/runtime-dom@3.5.34': + resolution: {integrity: sha512-e8kZzERmCwUnBRVsgSQlAfrfU2rGoy0FFKPBXSlfEjc/O3KfA7QP0t1/2ZylrbchjmIKB4dPTd07A6WPr0eOrg==} - '@vue/server-renderer@3.5.33': - resolution: {integrity: sha512-0xylq/8/h44lVG0pZFknv1XIdEgymq2E9n59uTWJBG+dIgiT0TMCSsxrN7nO16Z0MU0MPjFcguBbZV8Itk52Hw==} + '@vue/server-renderer@3.5.34': + resolution: {integrity: sha512-nHxmJoTrKsmrkbILRhkC9gY1G3moZbJTqCzDd7DOOzG5KH9oeJ0Unqrff5f9v0pW//jES05ZkJcNtfE8JjOIew==} peerDependencies: - vue: 3.5.33 + vue: 3.5.34 - '@vue/shared@3.5.33': - resolution: {integrity: sha512-5vR2QIlmaLG77Ygd4pMP6+SGQ5yox9VhtnbDWTy9DzMzdmeLxZ1QqxrywEZ9sa1AVubfIJyaCG3ytyWU81ufcQ==} + '@vue/shared@3.5.34': + resolution: {integrity: sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA==} '@vue/tsconfig@0.9.1': resolution: {integrity: sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==} @@ -1800,8 +1800,8 @@ packages: engines: {node: '>=10.0.0'} deprecated: this version has critical issues, please update to the latest version - ace-builds@1.43.6: - resolution: {integrity: sha512-L1ddibQ7F3vyXR2k2fg+I8TQTPWVA6CKeDQr/h2+8CeyTp3W6EQL8xNFZRTztuP8xNOAqL3IYPqdzs31GCjDvg==} + ace-builds@1.44.0: + resolution: {integrity: sha512-PFNMSYqFdEUkul2Ntud0HvA09AgY+F1ag0UYdpMH60wNI/qOA8cB8tlTgoALMEwIdUPJK2CjrIQ7OnbiSS/ugQ==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -1819,8 +1819,8 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} - alien-signals@3.1.2: - resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==} + alien-signals@3.2.1: + resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} @@ -1868,10 +1868,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - baseline-browser-mapping@2.9.19: - resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} - hasBin: true - birpc@2.9.0: resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} @@ -1881,8 +1877,8 @@ packages: brace-expansion@2.1.0: resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} - brace-expansion@5.0.5: - resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} braces@3.0.3: @@ -1896,11 +1892,6 @@ packages: peerDependencies: browserslist: '*' - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - browserslist@4.28.2: resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1999,11 +1990,8 @@ packages: dom-walk@0.1.2: resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} - dompurify@3.4.2: - resolution: {integrity: sha512-lHeS9SA/IKeIFFyYciHBr2n0v1VMPlSj843HdLOwjb2OxNwdq9Xykxqhk+FE42MzAdHvInbAolSE4mhahPpjXA==} - - electron-to-chromium@1.5.286: - resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==} + dompurify@3.4.4: + resolution: {integrity: sha512-r8K7KGKEcztXfA/nfabSYB2hg9tDphORJTdf8xprN/luSLGmNhOBN8dm1/SYjqLLet6YUFEXOcrdTuwryp/Bew==} electron-to-chromium@1.5.340: resolution: {integrity: sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==} @@ -2015,6 +2003,10 @@ packages: epubjs@0.3.93: resolution: {integrity: sha512-c06pNSdBxcXv3dZSbXAVLE1/pmleRhOT6mXNZo6INKmvuKpYB65MwU/lO7830czCtjIiK9i+KR+3S+p0wtljrw==} + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + es-module-lexer@2.1.0: resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} @@ -2072,14 +2064,14 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-vue@10.9.0: - resolution: {integrity: sha512-EFNNzu4HqtTRb5DJINpyd+u3bDdzETWDMpCzG+UBHz1tpsnMDCeOcf61u4Wy/cbXnMymK+MT9bjH7KcG1fItSw==} + eslint-plugin-vue@10.9.1: + resolution: {integrity: sha512-cHB0Tf4Duvzwecwd/AqWzZvF/QszE13BhjVUpVXWCy9AeMR5GjkAjP3i85vqgLgOuTmkHR1OJ5oMeqLHtuw8zg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 '@typescript-eslint/parser': ^7.0.0 || ^8.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - vue-eslint-parser: ^10.0.0 + vue-eslint-parser: ^10.3.0 peerDependenciesMeta: '@stylistic/eslint-plugin': optional: true @@ -2098,8 +2090,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.3.0: - resolution: {integrity: sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==} + eslint@10.4.0: + resolution: {integrity: sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -2243,8 +2235,8 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} engines: {node: '>= 0.4'} hookable@5.5.3: @@ -2268,8 +2260,8 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} engines: {node: '>= 0.4'} is-extglob@2.1.1: @@ -2557,9 +2549,6 @@ packages: next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - node-releases@2.0.37: resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} @@ -2685,8 +2674,8 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qrcode.vue@3.9.0: - resolution: {integrity: sha512-AxkgdNXd6R6N1rJcSKna3GfMz3cMpLZo95CLuF/3227nNY32iACBtyqRr6mJc3SW7w628Iy0vgnLWYqxHqgRLw==} + qrcode.vue@3.9.1: + resolution: {integrity: sha512-CpHVRz5iveqwRFh+nzzSYV9hPWU6q+YSOKyq5ZievjQIBv4bIIDzajGgtNz/yYSlczjAkYM3GNAQJHwwCukMEQ==} peerDependencies: vue: ^3.0.0 @@ -2723,15 +2712,15 @@ packages: regjsgen@0.8.0: resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} - regjsparser@0.13.0: - resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} + regjsparser@0.13.1: + resolution: {integrity: sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==} hasBin: true requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true @@ -2746,8 +2735,8 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rolldown@1.0.0-rc.17: - resolution: {integrity: sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==} + rolldown@1.0.1: + resolution: {integrity: sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -2774,6 +2763,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} @@ -2836,16 +2830,16 @@ packages: tar-mini@0.2.0: resolution: {integrity: sha512-+qfUHz700DWnRutdUsxRRVZ38G1Qr27OetwaMYTdg8hcPxf46U0S1Zf76dQMWRBmusOt2ZCK5kbIaiLkoGO7WQ==} - terser@5.46.2: - resolution: {integrity: sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw==} + terser@5.47.1: + resolution: {integrity: sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==} engines: {node: '>=10'} hasBin: true tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.1.1: - resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} + tinyexec@1.1.2: + resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} engines: {node: '>=18'} tinyglobby@0.2.16: @@ -2892,8 +2886,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - ufo@1.6.3: - resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} @@ -2971,13 +2965,13 @@ packages: vite-plugin-compression2@2.5.3: resolution: {integrity: sha512-ItPgqQWkcnBbVw7is9OKwiZ8v6+ju9rYROl5Lp6QfQDEx/d55AwJQb/KLpsQqsU9HoigYBsZ8tK6I02UwJNvEw==} - vite@8.0.10: - resolution: {integrity: sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==} + vite@8.0.13: + resolution: {integrity: sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.1.0 + '@vitejs/devtools': ^0.1.18 esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 @@ -3014,20 +3008,20 @@ packages: yaml: optional: true - vitest@4.1.5: - resolution: {integrity: sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==} + vitest@4.1.6: + resolution: {integrity: sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.5 - '@vitest/browser-preview': 4.1.5 - '@vitest/browser-webdriverio': 4.1.5 - '@vitest/coverage-istanbul': 4.1.5 - '@vitest/coverage-v8': 4.1.5 - '@vitest/ui': 4.1.5 + '@vitest/browser-playwright': 4.1.6 + '@vitest/browser-preview': 4.1.6 + '@vitest/browser-webdriverio': 4.1.6 + '@vitest/coverage-istanbul': 4.1.6 + '@vitest/coverage-v8': 4.1.6 + '@vitest/ui': 4.1.6 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3064,8 +3058,8 @@ packages: peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - vue-i18n@11.4.0: - resolution: {integrity: sha512-gxLVtcwdvOgwKSzkdb7nHKlW0N85A6aDNmHLnq6V+3w2/BXy/os5l71P7TIlgIQTxX0zJjiz89iImoHi51GieQ==} + vue-i18n@11.4.2: + resolution: {integrity: sha512-sADDeKXqAGsPX6tK3t3y2ZiMpbVWN12tG+MhTiJ06rVoh58eGtM4wFyw3uWGbVkXByVp9Ne/AP+nSSzI+J9OAQ==} engines: {node: '>= 16'} peerDependencies: vue: ^3.0.0 @@ -3076,13 +3070,13 @@ packages: vue-reader@1.3.4: resolution: {integrity: sha512-QYTX9hlrV71gL/1vMejcBLLS9Ool29XMZcLQwvL0Ep1F//o0ymzYbKX2Lre+4BUBkVq49/GmmGCmAJACsJL9tw==} - vue-router@5.0.6: - resolution: {integrity: sha512-9+kmUTGbKMyW9Asoy98IXXYIzrTMT7JDAdpDDeEkorHvybpUvBI2wsrSM5jFOXrFydpzRFJ9vAh+80DN2PGu9w==} + vue-router@5.0.7: + resolution: {integrity: sha512-dqfk8kvRbCutmCOCj/XLDqDEYxc1wBdAOGLuVy5M93ifYMsBd5fIjfaPN4tQAbxr5IprdBDIox1gr4wYyOx/SA==} peerDependencies: '@pinia/colada': '>=0.21.2' - '@vue/compiler-sfc': ^3.5.17 + '@vue/compiler-sfc': ^3.5.34 pinia: ^3.0.4 - vue: ^3.5.0 + vue: ^3.5.34 peerDependenciesMeta: '@pinia/colada': optional: true @@ -3096,14 +3090,14 @@ packages: peerDependencies: vue: ^3.0.2 - vue-tsc@3.2.8: - resolution: {integrity: sha512-27vTLJ6Q2370obOd0PFYoYoKnmXJ521uUIedrs3Zhhhg/8YG10VOCMmwt+JQslatpAMTDbnWiitLnoD5VlIvog==} + vue-tsc@3.2.9: + resolution: {integrity: sha512-qm8/nbo+9eZc1SCndm9wT+gq23pM+wRIdHY0wjm83B3lIginHTwcdrLUyTrKjDWXbMVNjKegNrnymhpdqnCL3A==} hasBin: true peerDependencies: typescript: '>=5.0.0' - vue@3.5.33: - resolution: {integrity: sha512-1AgChhx5w3ALgT4oK3acm2Es/7jyZhWSVUfs3rOBlGQC0rjEDkS7G4lWlJJGGNQD+BV3reCwbQrOe1mPNwKHBQ==} + vue@3.5.34: + resolution: {integrity: sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -3138,8 +3132,8 @@ packages: resolution: {integrity: sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==} engines: {node: ^14.17.0 || >=16.0.0} - yaml@2.8.3: - resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} hasBin: true @@ -3155,7 +3149,7 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.29.0': {} + '@babel/compat-data@7.29.3': {} '@babel/core@7.29.0': dependencies: @@ -3164,7 +3158,7 @@ snapshots: '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) '@babel/helpers': 7.29.2 - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.3 '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 @@ -3179,25 +3173,34 @@ snapshots: '@babel/generator@7.29.1': dependencies: - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.3 '@babel/types': 7.29.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@8.0.0-rc.5': + dependencies: + '@babel/parser': 8.0.0-rc.5 + '@babel/types': 8.0.0-rc.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': dependencies: '@babel/types': 7.29.0 '@babel/helper-compilation-targets@7.28.6': dependencies: - '@babel/compat-data': 7.29.0 + '@babel/compat-data': 7.29.3 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 + browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)': + '@babel/helper-create-class-features-plugin@7.29.3(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 @@ -3224,7 +3227,7 @@ snapshots: '@babel/helper-plugin-utils': 7.28.6 debug: 4.4.3 lodash.debounce: 4.0.8 - resolve: 1.22.11 + resolve: 1.22.12 transitivePeerDependencies: - supports-color @@ -3286,8 +3289,12 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@8.0.0-rc.5': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@8.0.0-rc.5': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helper-wrap-function@7.28.6': @@ -3303,10 +3310,14 @@ snapshots: '@babel/template': 7.28.6 '@babel/types': 7.29.0 - '@babel/parser@7.29.2': + '@babel/parser@7.29.3': dependencies: '@babel/types': 7.29.0 + '@babel/parser@8.0.0-rc.5': + dependencies: + '@babel/types': 8.0.0-rc.5 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -3325,6 +3336,14 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -3398,7 +3417,7 @@ snapshots: '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color @@ -3406,7 +3425,7 @@ snapshots: '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color @@ -3530,7 +3549,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-systemjs@7.29.4(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) @@ -3609,7 +3628,7 @@ snapshots: '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color @@ -3618,7 +3637,7 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color @@ -3695,9 +3714,9 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 - '@babel/preset-env@7.29.2(@babel/core@7.29.0)': + '@babel/preset-env@7.29.5(@babel/core@7.29.0)': dependencies: - '@babel/compat-data': 7.29.0 + '@babel/compat-data': 7.29.3 '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 @@ -3705,6 +3724,7 @@ snapshots: '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0) '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0) '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.3(@babel/core@7.29.0) '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0) '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0) '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0) @@ -3736,7 +3756,7 @@ snapshots: '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-modules-systemjs': 7.29.4(@babel/core@7.29.0) '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0) @@ -3783,7 +3803,7 @@ snapshots: '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.3 '@babel/types': 7.29.0 '@babel/traverse@7.29.0': @@ -3791,7 +3811,7 @@ snapshots: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.3 '@babel/template': 7.28.6 '@babel/types': 7.29.0 debug: 4.4.3 @@ -3803,9 +3823,14 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@chenfengyuan/vue-number-input@2.0.1(vue@3.5.33(typescript@5.9.3))': + '@babel/types@8.0.0-rc.5': + dependencies: + '@babel/helper-string-parser': 8.0.0-rc.5 + '@babel/helper-validator-identifier': 8.0.0-rc.5 + + '@chenfengyuan/vue-number-input@2.0.1(vue@3.5.34(typescript@5.9.3))': dependencies: - vue: 3.5.33(typescript@5.9.3) + vue: 3.5.34(typescript@5.9.3) '@emnapi/core@1.10.0': dependencies: @@ -3979,9 +4004,9 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.3.0)': + '@eslint-community/eslint-utils@4.9.1(eslint@10.4.0)': dependencies: - eslint: 10.3.0 + eslint: 10.4.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -3994,7 +4019,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.5.5': + '@eslint/config-helpers@0.6.0': dependencies: '@eslint/core': 1.2.1 @@ -4025,10 +4050,10 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@intlify/bundle-utils@11.1.2(vue-i18n@11.4.0(vue@3.5.33(typescript@5.9.3)))': + '@intlify/bundle-utils@11.2.3(vue-i18n@11.4.2(vue@3.5.34(typescript@5.9.3)))': dependencies: - '@intlify/message-compiler': 11.4.0 - '@intlify/shared': 11.4.0 + '@intlify/message-compiler': 11.4.2 + '@intlify/shared': 11.4.2 acorn: 8.16.0 esbuild: 0.25.12 escodegen: 2.1.0 @@ -4037,44 +4062,44 @@ snapshots: source-map-js: 1.2.1 yaml-eslint-parser: 1.3.2 optionalDependencies: - vue-i18n: 11.4.0(vue@3.5.33(typescript@5.9.3)) + vue-i18n: 11.4.2(vue@3.5.34(typescript@5.9.3)) - '@intlify/core-base@11.4.0': + '@intlify/core-base@11.4.2': dependencies: - '@intlify/devtools-types': 11.4.0 - '@intlify/message-compiler': 11.4.0 - '@intlify/shared': 11.4.0 + '@intlify/devtools-types': 11.4.2 + '@intlify/message-compiler': 11.4.2 + '@intlify/shared': 11.4.2 - '@intlify/devtools-types@11.4.0': + '@intlify/devtools-types@11.4.2': dependencies: - '@intlify/core-base': 11.4.0 - '@intlify/shared': 11.4.0 + '@intlify/core-base': 11.4.2 + '@intlify/shared': 11.4.2 - '@intlify/message-compiler@11.4.0': + '@intlify/message-compiler@11.4.2': dependencies: - '@intlify/shared': 11.4.0 + '@intlify/shared': 11.4.2 source-map-js: 1.2.1 - '@intlify/shared@11.4.0': {} + '@intlify/shared@11.4.2': {} - '@intlify/unplugin-vue-i18n@11.1.2(@vue/compiler-dom@3.5.33)(eslint@10.3.0)(rollup@4.57.1)(typescript@5.9.3)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3))(vue-i18n@11.4.0(vue@3.5.33(typescript@5.9.3)))(vue@3.5.33(typescript@5.9.3))': + '@intlify/unplugin-vue-i18n@11.2.3(@vue/compiler-dom@3.5.34)(eslint@10.4.0)(rollup@4.57.1)(typescript@5.9.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0))(vue-i18n@11.4.2(vue@3.5.34(typescript@5.9.3)))(vue@3.5.34(typescript@5.9.3))': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0) - '@intlify/bundle-utils': 11.1.2(vue-i18n@11.4.0(vue@3.5.33(typescript@5.9.3))) - '@intlify/shared': 11.4.0 - '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.4.0)(@vue/compiler-dom@3.5.33)(vue-i18n@11.4.0(vue@3.5.33(typescript@5.9.3)))(vue@3.5.33(typescript@5.9.3)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.0) + '@intlify/bundle-utils': 11.2.3(vue-i18n@11.4.2(vue@3.5.34(typescript@5.9.3))) + '@intlify/shared': 11.4.2 + '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.4.2)(@vue/compiler-dom@3.5.34)(vue-i18n@11.4.2(vue@3.5.34(typescript@5.9.3)))(vue@3.5.34(typescript@5.9.3)) '@rollup/pluginutils': 5.3.0(rollup@4.57.1) - '@typescript-eslint/scope-manager': 8.59.1 - '@typescript-eslint/typescript-estree': 8.59.1(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) debug: 4.4.3 fast-glob: 3.3.3 pathe: 2.0.3 picocolors: 1.1.1 unplugin: 2.3.11 - vue: 3.5.33(typescript@5.9.3) + vue: 3.5.34(typescript@5.9.3) optionalDependencies: - vite: 8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3) - vue-i18n: 11.4.0(vue@3.5.33(typescript@5.9.3)) + vite: 8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0) + vue-i18n: 11.4.2(vue@3.5.34(typescript@5.9.3)) transitivePeerDependencies: - '@vue/compiler-dom' - eslint @@ -4082,14 +4107,14 @@ snapshots: - supports-color - typescript - '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.4.0)(@vue/compiler-dom@3.5.33)(vue-i18n@11.4.0(vue@3.5.33(typescript@5.9.3)))(vue@3.5.33(typescript@5.9.3))': + '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.4.2)(@vue/compiler-dom@3.5.34)(vue-i18n@11.4.2(vue@3.5.34(typescript@5.9.3)))(vue@3.5.34(typescript@5.9.3))': dependencies: - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.3 optionalDependencies: - '@intlify/shared': 11.4.0 - '@vue/compiler-dom': 3.5.33 - vue: 3.5.33(typescript@5.9.3) - vue-i18n: 11.4.0(vue@3.5.33(typescript@5.9.3)) + '@intlify/shared': 11.4.2 + '@vue/compiler-dom': 3.5.34 + vue: 3.5.34(typescript@5.9.3) + vue-i18n: 11.4.2(vue@3.5.34(typescript@5.9.3)) '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -4119,7 +4144,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.1 + '@tybys/wasm-util': 0.10.2 optional: true '@nodelib/fs.scandir@2.1.5': @@ -4134,62 +4159,60 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@oxc-project/types@0.127.0': {} + '@oxc-project/types@0.130.0': {} '@pkgr/core@0.2.9': {} - '@rolldown/binding-android-arm64@1.0.0-rc.17': + '@rolldown/binding-android-arm64@1.0.1': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.17': + '@rolldown/binding-darwin-arm64@1.0.1': optional: true - '@rolldown/binding-darwin-x64@1.0.0-rc.17': + '@rolldown/binding-darwin-x64@1.0.1': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-rc.17': + '@rolldown/binding-freebsd-x64@1.0.1': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': + '@rolldown/binding-linux-arm-gnueabihf@1.0.1': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': + '@rolldown/binding-linux-arm64-gnu@1.0.1': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': + '@rolldown/binding-linux-arm64-musl@1.0.1': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': + '@rolldown/binding-linux-ppc64-gnu@1.0.1': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': + '@rolldown/binding-linux-s390x-gnu@1.0.1': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': + '@rolldown/binding-linux-x64-gnu@1.0.1': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': + '@rolldown/binding-linux-x64-musl@1.0.1': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': + '@rolldown/binding-openharmony-arm64@1.0.1': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': + '@rolldown/binding-wasm32-wasi@1.0.1': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': + '@rolldown/binding-win32-arm64-msvc@1.0.1': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': + '@rolldown/binding-win32-x64-msvc@1.0.1': optional: true - '@rolldown/pluginutils@1.0.0-rc.13': {} - - '@rolldown/pluginutils@1.0.0-rc.17': {} + '@rolldown/pluginutils@1.0.1': {} '@rollup/pluginutils@5.3.0(rollup@4.57.1)': dependencies: @@ -4278,7 +4301,7 @@ snapshots: '@tsconfig/node24@24.0.4': {} - '@tybys/wasm-util@0.10.1': + '@tybys/wasm-util@0.10.2': dependencies: tslib: 2.8.1 optional: true @@ -4294,6 +4317,10 @@ snapshots: '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} + + '@types/jsesc@2.5.1': {} + '@types/json-schema@7.0.15': {} '@types/localforage@0.0.34': @@ -4306,7 +4333,7 @@ snapshots: '@types/lodash@4.17.23': {} - '@types/node@24.12.2': + '@types/node@24.12.4': dependencies: undici-types: 7.16.0 @@ -4315,15 +4342,15 @@ snapshots: '@types/web-bluetooth@0.0.21': {} - '@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@10.3.0)(typescript@5.9.3))(eslint@10.3.0)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@10.4.0)(typescript@5.9.3))(eslint@10.4.0)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.56.0(eslint@10.3.0)(typescript@5.9.3) + '@typescript-eslint/parser': 8.56.0(eslint@10.4.0)(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.56.0 - '@typescript-eslint/type-utils': 8.56.0(eslint@10.3.0)(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.0(eslint@10.3.0)(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.56.0(eslint@10.4.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.0(eslint@10.4.0)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.56.0 - eslint: 10.3.0 + eslint: 10.4.0 ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@5.9.3) @@ -4331,15 +4358,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.56.0(eslint@10.3.0)(typescript@5.9.3))(eslint@10.3.0)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.56.0(eslint@10.4.0)(typescript@5.9.3))(eslint@10.4.0)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.56.0(eslint@10.3.0)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.59.2 - '@typescript-eslint/type-utils': 8.59.2(eslint@10.3.0)(typescript@5.9.3) - '@typescript-eslint/utils': 8.59.2(eslint@10.3.0)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.59.2 - eslint: 10.3.0 + '@typescript-eslint/parser': 8.56.0(eslint@10.4.0)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/type-utils': 8.59.3(eslint@10.4.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.3(eslint@10.4.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.3 + eslint: 10.4.0 ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@5.9.3) @@ -4347,14 +4374,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.56.0(eslint@10.3.0)(typescript@5.9.3)': + '@typescript-eslint/parser@8.56.0(eslint@10.4.0)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.56.0 '@typescript-eslint/types': 8.56.0 '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.56.0 debug: 4.4.3 - eslint: 10.3.0 + eslint: 10.4.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -4368,19 +4395,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.59.1(typescript@5.9.3)': + '@typescript-eslint/project-service@8.59.3(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.1(typescript@5.9.3) - '@typescript-eslint/types': 8.59.1 - debug: 4.4.3 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.59.2(typescript@5.9.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) - '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.9.3) + '@typescript-eslint/types': 8.59.3 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -4391,47 +4409,38 @@ snapshots: '@typescript-eslint/types': 8.56.0 '@typescript-eslint/visitor-keys': 8.56.0 - '@typescript-eslint/scope-manager@8.59.1': - dependencies: - '@typescript-eslint/types': 8.59.1 - '@typescript-eslint/visitor-keys': 8.59.1 - - '@typescript-eslint/scope-manager@8.59.2': + '@typescript-eslint/scope-manager@8.59.3': dependencies: - '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/visitor-keys': 8.59.2 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/visitor-keys': 8.59.3 '@typescript-eslint/tsconfig-utils@8.56.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.59.1(typescript@5.9.3)': - dependencies: - typescript: 5.9.3 - - '@typescript-eslint/tsconfig-utils@8.59.2(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.59.3(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.56.0(eslint@10.3.0)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.56.0(eslint@10.4.0)(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.56.0 '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.0(eslint@10.3.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.0(eslint@10.4.0)(typescript@5.9.3) debug: 4.4.3 - eslint: 10.3.0 + eslint: 10.4.0 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.59.2(eslint@10.3.0)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.59.3(eslint@10.4.0)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) - '@typescript-eslint/utils': 8.59.2(eslint@10.3.0)(typescript@5.9.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.3(eslint@10.4.0)(typescript@5.9.3) debug: 4.4.3 - eslint: 10.3.0 + eslint: 10.4.0 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -4439,9 +4448,7 @@ snapshots: '@typescript-eslint/types@8.56.0': {} - '@typescript-eslint/types@8.59.1': {} - - '@typescript-eslint/types@8.59.2': {} + '@typescript-eslint/types@8.59.3': {} '@typescript-eslint/typescript-estree@8.56.0(typescript@5.9.3)': dependencies: @@ -4458,54 +4465,39 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.59.1(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.59.3(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.59.1(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.59.1(typescript@5.9.3) - '@typescript-eslint/types': 8.59.1 - '@typescript-eslint/visitor-keys': 8.59.1 + '@typescript-eslint/project-service': 8.59.3(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.9.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/visitor-keys': 8.59.3 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.7.4 + semver: 7.8.0 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.59.2(typescript@5.9.3)': + '@typescript-eslint/utils@8.56.0(eslint@10.4.0)(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.59.2(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) - '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/visitor-keys': 8.59.2 - debug: 4.4.3 - minimatch: 10.2.5 - semver: 7.7.4 - tinyglobby: 0.2.16 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.56.0(eslint@10.3.0)(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.0) '@typescript-eslint/scope-manager': 8.56.0 '@typescript-eslint/types': 8.56.0 '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3) - eslint: 10.3.0 + eslint: 10.4.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.2(eslint@10.3.0)(typescript@5.9.3)': + '@typescript-eslint/utils@8.59.3(eslint@10.4.0)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0) - '@typescript-eslint/scope-manager': 8.59.2 - '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) - eslint: 10.3.0 + '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.0) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) + eslint: 10.4.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -4515,14 +4507,9 @@ snapshots: '@typescript-eslint/types': 8.56.0 eslint-visitor-keys: 5.0.1 - '@typescript-eslint/visitor-keys@8.59.1': - dependencies: - '@typescript-eslint/types': 8.59.1 - eslint-visitor-keys: 5.0.1 - - '@typescript-eslint/visitor-keys@8.59.2': + '@typescript-eslint/visitor-keys@8.59.3': dependencies: - '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/types': 8.59.3 eslint-visitor-keys: 5.0.1 '@videojs/http-streaming@3.17.4(video.js@8.23.7)': @@ -4547,69 +4534,69 @@ snapshots: global: 4.4.0 is-function: 1.0.2 - '@vitejs/plugin-legacy@8.0.1(terser@5.46.2)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3))': + '@vitejs/plugin-legacy@8.0.2(terser@5.47.1)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.29.0) - '@babel/preset-env': 7.29.2(@babel/core@7.29.0) + '@babel/plugin-transform-modules-systemjs': 7.29.4(@babel/core@7.29.0) + '@babel/preset-env': 7.29.5(@babel/core@7.29.0) babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.0) babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) - browserslist: 4.28.1 - browserslist-to-esbuild: 2.1.1(browserslist@4.28.1) + browserslist: 4.28.2 + browserslist-to-esbuild: 2.1.1(browserslist@4.28.2) core-js: 3.49.0 magic-string: 0.30.21 regenerator-runtime: 0.14.1 systemjs: 6.15.1 - terser: 5.46.2 - vite: 8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3) + terser: 5.47.1 + vite: 8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@6.0.6(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3))(vue@3.5.33(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.7(vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0))(vue@3.5.34(typescript@5.9.3))': dependencies: - '@rolldown/pluginutils': 1.0.0-rc.13 - vite: 8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3) - vue: 3.5.33(typescript@5.9.3) + '@rolldown/pluginutils': 1.0.1 + vite: 8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0) + vue: 3.5.34(typescript@5.9.3) - '@vitest/expect@4.1.5': + '@vitest/expect@4.1.6': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.5 - '@vitest/utils': 4.1.5 + '@vitest/spy': 4.1.6 + '@vitest/utils': 4.1.6 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.5(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3))': + '@vitest/mocker@4.1.6(vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.1.5 + '@vitest/spy': 4.1.6 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3) + vite: 8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0) - '@vitest/pretty-format@4.1.5': + '@vitest/pretty-format@4.1.6': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.5': + '@vitest/runner@4.1.6': dependencies: - '@vitest/utils': 4.1.5 + '@vitest/utils': 4.1.6 pathe: 2.0.3 - '@vitest/snapshot@4.1.5': + '@vitest/snapshot@4.1.6': dependencies: - '@vitest/pretty-format': 4.1.5 - '@vitest/utils': 4.1.5 + '@vitest/pretty-format': 4.1.6 + '@vitest/utils': 4.1.6 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.5': {} + '@vitest/spy@4.1.6': {} - '@vitest/utils@4.1.5': + '@vitest/utils@4.1.6': dependencies: - '@vitest/pretty-format': 4.1.5 + '@vitest/pretty-format': 4.1.6 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -4625,45 +4612,45 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.1.0 - '@vue-macros/common@3.1.2(vue@3.5.33(typescript@5.9.3))': + '@vue-macros/common@3.1.2(vue@3.5.34(typescript@5.9.3))': dependencies: - '@vue/compiler-sfc': 3.5.33 + '@vue/compiler-sfc': 3.5.34 ast-kit: 2.2.0 local-pkg: 1.1.2 magic-string-ast: 1.0.3 unplugin-utils: 0.3.1 optionalDependencies: - vue: 3.5.33(typescript@5.9.3) + vue: 3.5.34(typescript@5.9.3) - '@vue/compiler-core@3.5.33': + '@vue/compiler-core@3.5.34': dependencies: - '@babel/parser': 7.29.2 - '@vue/shared': 3.5.33 + '@babel/parser': 7.29.3 + '@vue/shared': 3.5.34 entities: 7.0.1 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.33': + '@vue/compiler-dom@3.5.34': dependencies: - '@vue/compiler-core': 3.5.33 - '@vue/shared': 3.5.33 + '@vue/compiler-core': 3.5.34 + '@vue/shared': 3.5.34 - '@vue/compiler-sfc@3.5.33': + '@vue/compiler-sfc@3.5.34': dependencies: - '@babel/parser': 7.29.2 - '@vue/compiler-core': 3.5.33 - '@vue/compiler-dom': 3.5.33 - '@vue/compiler-ssr': 3.5.33 - '@vue/shared': 3.5.33 + '@babel/parser': 7.29.3 + '@vue/compiler-core': 3.5.34 + '@vue/compiler-dom': 3.5.34 + '@vue/compiler-ssr': 3.5.34 + '@vue/shared': 3.5.34 estree-walker: 2.0.2 magic-string: 0.30.21 postcss: 8.5.14 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.33': + '@vue/compiler-ssr@3.5.34': dependencies: - '@vue/compiler-dom': 3.5.33 - '@vue/shared': 3.5.33 + '@vue/compiler-dom': 3.5.34 + '@vue/shared': 3.5.34 '@vue/devtools-api@6.6.4': {} @@ -4671,9 +4658,9 @@ snapshots: dependencies: '@vue/devtools-kit': 7.7.9 - '@vue/devtools-api@8.1.1': + '@vue/devtools-api@8.1.2': dependencies: - '@vue/devtools-kit': 8.1.1 + '@vue/devtools-kit': 8.1.2 '@vue/devtools-kit@7.7.9': dependencies: @@ -4685,9 +4672,9 @@ snapshots: speakingurl: 14.0.1 superjson: 2.2.6 - '@vue/devtools-kit@8.1.1': + '@vue/devtools-kit@8.1.2': dependencies: - '@vue/devtools-shared': 8.1.1 + '@vue/devtools-shared': 8.1.2 birpc: 2.9.0 hookable: 5.5.3 perfect-debounce: 2.1.0 @@ -4696,96 +4683,96 @@ snapshots: dependencies: rfdc: 1.4.1 - '@vue/devtools-shared@8.1.1': {} + '@vue/devtools-shared@8.1.2': {} - '@vue/eslint-config-prettier@10.2.0(eslint@10.3.0)(prettier@3.8.3)': + '@vue/eslint-config-prettier@10.2.0(eslint@10.4.0)(prettier@3.8.3)': dependencies: - eslint: 10.3.0 - eslint-config-prettier: 10.1.8(eslint@10.3.0) - eslint-plugin-prettier: 5.5.5(eslint-config-prettier@10.1.8(eslint@10.3.0))(eslint@10.3.0)(prettier@3.8.3) + eslint: 10.4.0 + eslint-config-prettier: 10.1.8(eslint@10.4.0) + eslint-plugin-prettier: 5.5.5(eslint-config-prettier@10.1.8(eslint@10.4.0))(eslint@10.4.0)(prettier@3.8.3) prettier: 3.8.3 transitivePeerDependencies: - '@types/eslint' - '@vue/eslint-config-typescript@14.7.0(eslint-plugin-vue@10.9.0(@typescript-eslint/parser@8.56.0(eslint@10.3.0)(typescript@5.9.3))(eslint@10.3.0)(vue-eslint-parser@10.4.0(eslint@10.3.0)))(eslint@10.3.0)(typescript@5.9.3)': + '@vue/eslint-config-typescript@14.7.0(eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.56.0(eslint@10.4.0)(typescript@5.9.3))(eslint@10.4.0)(vue-eslint-parser@10.4.0(eslint@10.4.0)))(eslint@10.4.0)(typescript@5.9.3)': dependencies: - '@typescript-eslint/utils': 8.56.0(eslint@10.3.0)(typescript@5.9.3) - eslint: 10.3.0 - eslint-plugin-vue: 10.9.0(@typescript-eslint/parser@8.56.0(eslint@10.3.0)(typescript@5.9.3))(eslint@10.3.0)(vue-eslint-parser@10.4.0(eslint@10.3.0)) + '@typescript-eslint/utils': 8.56.0(eslint@10.4.0)(typescript@5.9.3) + eslint: 10.4.0 + eslint-plugin-vue: 10.9.1(@typescript-eslint/parser@8.56.0(eslint@10.4.0)(typescript@5.9.3))(eslint@10.4.0)(vue-eslint-parser@10.4.0(eslint@10.4.0)) fast-glob: 3.3.3 - typescript-eslint: 8.56.0(eslint@10.3.0)(typescript@5.9.3) - vue-eslint-parser: 10.4.0(eslint@10.3.0) + typescript-eslint: 8.56.0(eslint@10.4.0)(typescript@5.9.3) + vue-eslint-parser: 10.4.0(eslint@10.4.0) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@vue/language-core@3.2.8': + '@vue/language-core@3.2.9': dependencies: '@volar/language-core': 2.4.28 - '@vue/compiler-dom': 3.5.33 - '@vue/shared': 3.5.33 - alien-signals: 3.1.2 + '@vue/compiler-dom': 3.5.34 + '@vue/shared': 3.5.34 + alien-signals: 3.2.1 muggle-string: 0.4.1 path-browserify: 1.0.1 picomatch: 4.0.4 - '@vue/reactivity@3.5.33': + '@vue/reactivity@3.5.34': dependencies: - '@vue/shared': 3.5.33 + '@vue/shared': 3.5.34 - '@vue/runtime-core@3.5.33': + '@vue/runtime-core@3.5.34': dependencies: - '@vue/reactivity': 3.5.33 - '@vue/shared': 3.5.33 + '@vue/reactivity': 3.5.34 + '@vue/shared': 3.5.34 - '@vue/runtime-dom@3.5.33': + '@vue/runtime-dom@3.5.34': dependencies: - '@vue/reactivity': 3.5.33 - '@vue/runtime-core': 3.5.33 - '@vue/shared': 3.5.33 + '@vue/reactivity': 3.5.34 + '@vue/runtime-core': 3.5.34 + '@vue/shared': 3.5.34 csstype: 3.2.3 - '@vue/server-renderer@3.5.33(vue@3.5.33(typescript@5.9.3))': + '@vue/server-renderer@3.5.34(vue@3.5.34(typescript@5.9.3))': dependencies: - '@vue/compiler-ssr': 3.5.33 - '@vue/shared': 3.5.33 - vue: 3.5.33(typescript@5.9.3) + '@vue/compiler-ssr': 3.5.34 + '@vue/shared': 3.5.34 + vue: 3.5.34(typescript@5.9.3) - '@vue/shared@3.5.33': {} + '@vue/shared@3.5.34': {} - '@vue/tsconfig@0.9.1(typescript@5.9.3)(vue@3.5.33(typescript@5.9.3))': + '@vue/tsconfig@0.9.1(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3))': optionalDependencies: typescript: 5.9.3 - vue: 3.5.33(typescript@5.9.3) + vue: 3.5.34(typescript@5.9.3) - '@vueuse/core@14.3.0(vue@3.5.33(typescript@5.9.3))': + '@vueuse/core@14.3.0(vue@3.5.34(typescript@5.9.3))': dependencies: '@types/web-bluetooth': 0.0.21 '@vueuse/metadata': 14.3.0 - '@vueuse/shared': 14.3.0(vue@3.5.33(typescript@5.9.3)) - vue: 3.5.33(typescript@5.9.3) + '@vueuse/shared': 14.3.0(vue@3.5.34(typescript@5.9.3)) + vue: 3.5.34(typescript@5.9.3) - '@vueuse/integrations@14.3.0(focus-trap@8.0.0)(jwt-decode@4.0.0)(vue@3.5.33(typescript@5.9.3))': + '@vueuse/integrations@14.3.0(focus-trap@8.0.0)(jwt-decode@4.0.0)(vue@3.5.34(typescript@5.9.3))': dependencies: - '@vueuse/core': 14.3.0(vue@3.5.33(typescript@5.9.3)) - '@vueuse/shared': 14.3.0(vue@3.5.33(typescript@5.9.3)) - vue: 3.5.33(typescript@5.9.3) + '@vueuse/core': 14.3.0(vue@3.5.34(typescript@5.9.3)) + '@vueuse/shared': 14.3.0(vue@3.5.34(typescript@5.9.3)) + vue: 3.5.34(typescript@5.9.3) optionalDependencies: focus-trap: 8.0.0 jwt-decode: 4.0.0 '@vueuse/metadata@14.3.0': {} - '@vueuse/shared@14.3.0(vue@3.5.33(typescript@5.9.3))': + '@vueuse/shared@14.3.0(vue@3.5.34(typescript@5.9.3))': dependencies: - vue: 3.5.33(typescript@5.9.3) + vue: 3.5.34(typescript@5.9.3) '@xmldom/xmldom@0.7.13': {} '@xmldom/xmldom@0.8.11': {} - ace-builds@1.43.6: {} + ace-builds@1.44.0: {} acorn-jsx@5.3.2(acorn@8.16.0): dependencies: @@ -4807,18 +4794,18 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - alien-signals@3.1.2: {} + alien-signals@3.2.1: {} assertion-error@2.0.1: {} ast-kit@2.2.0: dependencies: - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.3 pathe: 2.0.3 ast-walker-scope@0.8.3: dependencies: - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.3 ast-kit: 2.2.0 autoprefixer@10.5.0(postcss@8.5.14): @@ -4832,7 +4819,7 @@ snapshots: babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0): dependencies: - '@babel/compat-data': 7.29.0 + '@babel/compat-data': 7.29.3 '@babel/core': 7.29.0 '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) semver: 6.3.1 @@ -4860,8 +4847,6 @@ snapshots: baseline-browser-mapping@2.10.19: {} - baseline-browser-mapping@2.9.19: {} - birpc@2.9.0: {} boolbase@1.0.0: {} @@ -4870,7 +4855,7 @@ snapshots: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.5: + brace-expansion@5.0.6: dependencies: balanced-match: 4.0.4 @@ -4878,19 +4863,11 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist-to-esbuild@2.1.1(browserslist@4.28.1): + browserslist-to-esbuild@2.1.1(browserslist@4.28.2): dependencies: - browserslist: 4.28.1 + browserslist: 4.28.2 meow: 13.2.0 - browserslist@4.28.1: - dependencies: - baseline-browser-mapping: 2.9.19 - caniuse-lite: 1.0.30001788 - electron-to-chromium: 1.5.286 - node-releases: 2.0.27 - update-browserslist-db: 1.2.3(browserslist@4.28.1) - browserslist@4.28.2: dependencies: baseline-browser-mapping: 2.10.19 @@ -4930,7 +4907,7 @@ snapshots: core-js-compat@3.49.0: dependencies: - browserslist: 4.28.1 + browserslist: 4.28.2 core-js@3.48.0: {} @@ -4969,12 +4946,10 @@ snapshots: dom-walk@0.1.2: {} - dompurify@3.4.2: + dompurify@3.4.4: optionalDependencies: '@types/trusted-types': 2.0.7 - electron-to-chromium@1.5.286: {} - electron-to-chromium@1.5.340: {} entities@7.0.1: {} @@ -4991,6 +4966,8 @@ snapshots: marks-pane: 1.0.9 path-webpack: 0.0.3 + es-errors@1.3.0: {} + es-module-lexer@2.1.0: {} es5-ext@0.10.64: @@ -5082,31 +5059,31 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-prettier@10.1.8(eslint@10.3.0): + eslint-config-prettier@10.1.8(eslint@10.4.0): dependencies: - eslint: 10.3.0 + eslint: 10.4.0 - eslint-plugin-prettier@5.5.5(eslint-config-prettier@10.1.8(eslint@10.3.0))(eslint@10.3.0)(prettier@3.8.3): + eslint-plugin-prettier@5.5.5(eslint-config-prettier@10.1.8(eslint@10.4.0))(eslint@10.4.0)(prettier@3.8.3): dependencies: - eslint: 10.3.0 + eslint: 10.4.0 prettier: 3.8.3 prettier-linter-helpers: 1.0.1 synckit: 0.11.12 optionalDependencies: - eslint-config-prettier: 10.1.8(eslint@10.3.0) + eslint-config-prettier: 10.1.8(eslint@10.4.0) - eslint-plugin-vue@10.9.0(@typescript-eslint/parser@8.56.0(eslint@10.3.0)(typescript@5.9.3))(eslint@10.3.0)(vue-eslint-parser@10.4.0(eslint@10.3.0)): + eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.56.0(eslint@10.4.0)(typescript@5.9.3))(eslint@10.4.0)(vue-eslint-parser@10.4.0(eslint@10.4.0)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0) - eslint: 10.3.0 + '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.0) + eslint: 10.4.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 7.1.1 - semver: 7.7.4 - vue-eslint-parser: 10.4.0(eslint@10.3.0) + semver: 7.8.0 + vue-eslint-parser: 10.4.0(eslint@10.4.0) xml-name-validator: 4.0.0 optionalDependencies: - '@typescript-eslint/parser': 8.56.0(eslint@10.3.0)(typescript@5.9.3) + '@typescript-eslint/parser': 8.56.0(eslint@10.4.0)(typescript@5.9.3) eslint-scope@9.1.2: dependencies: @@ -5119,18 +5096,18 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.3.0: + eslint@10.4.0: dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.0) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 - '@eslint/config-helpers': 0.5.5 + '@eslint/config-helpers': 0.6.0 '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.1 '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 ajv: 6.15.0 cross-spawn: 7.0.6 debug: 4.4.3 @@ -5189,7 +5166,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 esutils@2.0.3: {} @@ -5281,7 +5258,7 @@ snapshots: graceful-fs@4.2.11: {} - hasown@2.0.2: + hasown@2.0.3: dependencies: function-bind: 1.1.2 @@ -5297,9 +5274,9 @@ snapshots: inherits@2.0.4: {} - is-core-module@2.16.1: + is-core-module@2.16.2: dependencies: - hasown: 2.0.2 + hasown: 2.0.3 is-extglob@2.1.1: {} @@ -5338,7 +5315,7 @@ snapshots: acorn: 8.16.0 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - semver: 7.7.4 + semver: 7.8.0 jszip@3.10.1: dependencies: @@ -5509,7 +5486,7 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.5 + brace-expansion: 5.0.6 minimatch@9.0.9: dependencies: @@ -5522,7 +5499,7 @@ snapshots: acorn: 8.16.0 pathe: 2.0.3 pkg-types: 1.3.1 - ufo: 1.6.3 + ufo: 1.6.4 mpd-parser@1.3.1: dependencies: @@ -5546,8 +5523,6 @@ snapshots: next-tick@1.1.0: {} - node-releases@2.0.27: {} - node-releases@2.0.37: {} normalize.css@8.0.1: {} @@ -5599,10 +5574,10 @@ snapshots: picomatch@4.0.4: {} - pinia@3.0.4(typescript@5.9.3)(vue@3.5.33(typescript@5.9.3)): + pinia@3.0.4(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)): dependencies: '@vue/devtools-api': 7.7.9 - vue: 3.5.33(typescript@5.9.3) + vue: 3.5.34(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -5657,9 +5632,9 @@ snapshots: punycode@2.3.1: {} - qrcode.vue@3.9.0(vue@3.5.33(typescript@5.9.3)): + qrcode.vue@3.9.1(vue@3.5.34(typescript@5.9.3)): dependencies: - vue: 3.5.33(typescript@5.9.3) + vue: 3.5.34(typescript@5.9.3) quansync@0.2.11: {} @@ -5692,21 +5667,22 @@ snapshots: regenerate: 1.4.2 regenerate-unicode-properties: 10.2.2 regjsgen: 0.8.0 - regjsparser: 0.13.0 + regjsparser: 0.13.1 unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.2.1 regjsgen@0.8.0: {} - regjsparser@0.13.0: + regjsparser@0.13.1: dependencies: jsesc: 3.1.0 requires-port@1.0.0: {} - resolve@1.22.11: + resolve@1.22.12: dependencies: - is-core-module: 2.16.1 + es-errors: 1.3.0 + is-core-module: 2.16.2 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -5716,26 +5692,26 @@ snapshots: rfdc@1.4.1: {} - rolldown@1.0.0-rc.17: + rolldown@1.0.1: dependencies: - '@oxc-project/types': 0.127.0 - '@rolldown/pluginutils': 1.0.0-rc.17 + '@oxc-project/types': 0.130.0 + '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.17 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.17 - '@rolldown/binding-darwin-x64': 1.0.0-rc.17 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.17 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.17 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.17 - '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.17 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.17 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.17 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.17 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17 + '@rolldown/binding-android-arm64': 1.0.1 + '@rolldown/binding-darwin-arm64': 1.0.1 + '@rolldown/binding-darwin-x64': 1.0.1 + '@rolldown/binding-freebsd-x64': 1.0.1 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.1 + '@rolldown/binding-linux-arm64-gnu': 1.0.1 + '@rolldown/binding-linux-arm64-musl': 1.0.1 + '@rolldown/binding-linux-ppc64-gnu': 1.0.1 + '@rolldown/binding-linux-s390x-gnu': 1.0.1 + '@rolldown/binding-linux-x64-gnu': 1.0.1 + '@rolldown/binding-linux-x64-musl': 1.0.1 + '@rolldown/binding-openharmony-arm64': 1.0.1 + '@rolldown/binding-wasm32-wasi': 1.0.1 + '@rolldown/binding-win32-arm64-msvc': 1.0.1 + '@rolldown/binding-win32-x64-msvc': 1.0.1 rollup@4.57.1: dependencies: @@ -5781,6 +5757,8 @@ snapshots: semver@7.7.4: {} + semver@7.8.0: {} + setimmediate@1.0.5: {} shebang-command@2.0.0: @@ -5829,7 +5807,7 @@ snapshots: tar-mini@0.2.0: {} - terser@5.46.2: + terser@5.47.1: dependencies: '@jridgewell/source-map': 0.3.11 acorn: 8.16.0 @@ -5838,7 +5816,7 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.1.1: {} + tinyexec@1.1.2: {} tinyglobby@0.2.16: dependencies: @@ -5874,20 +5852,20 @@ snapshots: type@2.7.3: {} - typescript-eslint@8.56.0(eslint@10.3.0)(typescript@5.9.3): + typescript-eslint@8.56.0(eslint@10.4.0)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.56.0(@typescript-eslint/parser@8.56.0(eslint@10.3.0)(typescript@5.9.3))(eslint@10.3.0)(typescript@5.9.3) - '@typescript-eslint/parser': 8.56.0(eslint@10.3.0)(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.56.0(@typescript-eslint/parser@8.56.0(eslint@10.4.0)(typescript@5.9.3))(eslint@10.4.0)(typescript@5.9.3) + '@typescript-eslint/parser': 8.56.0(eslint@10.4.0)(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.0(eslint@10.3.0)(typescript@5.9.3) - eslint: 10.3.0 + '@typescript-eslint/utils': 8.56.0(eslint@10.4.0)(typescript@5.9.3) + eslint: 10.4.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color typescript@5.9.3: {} - ufo@1.6.3: {} + ufo@1.6.4: {} undici-types@7.16.0: {} @@ -5920,12 +5898,6 @@ snapshots: picomatch: 4.0.4 webpack-virtual-modules: 0.6.2 - update-browserslist-db@1.2.3(browserslist@4.28.1): - dependencies: - browserslist: 4.28.1 - escalade: 3.2.0 - picocolors: 1.1.1 - update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: browserslist: 4.28.2 @@ -5987,29 +5959,29 @@ snapshots: transitivePeerDependencies: - rollup - vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3): + vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.14 - rolldown: 1.0.0-rc.17 + rolldown: 1.0.1 tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 24.12.2 + '@types/node': 24.12.4 esbuild: 0.27.3 fsevents: 2.3.3 - terser: 5.46.2 - yaml: 2.8.3 - - vitest@4.1.5(@types/node@24.12.2)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3)): - dependencies: - '@vitest/expect': 4.1.5 - '@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.5 - '@vitest/runner': 4.1.5 - '@vitest/snapshot': 4.1.5 - '@vitest/spy': 4.1.5 - '@vitest/utils': 4.1.5 + terser: 5.47.1 + yaml: 2.9.0 + + vitest@4.1.6(@types/node@24.12.4)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.6 + '@vitest/mocker': 4.1.6(vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.6 + '@vitest/runner': 4.1.6 + '@vitest/snapshot': 4.1.6 + '@vitest/spy': 4.1.6 + '@vitest/utils': 4.1.6 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -6018,22 +5990,22 @@ snapshots: picomatch: 4.0.4 std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 1.1.1 + tinyexec: 1.1.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 8.0.10(@types/node@24.12.2)(esbuild@0.27.3)(terser@5.46.2)(yaml@2.8.3) + vite: 8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(terser@5.47.1)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.12.2 + '@types/node': 24.12.4 transitivePeerDependencies: - msw vscode-uri@3.1.0: {} - vue-eslint-parser@10.4.0(eslint@10.3.0): + vue-eslint-parser@10.4.0(eslint@10.4.0): dependencies: debug: 4.4.3 - eslint: 10.3.0 + eslint: 10.4.0 eslint-scope: 9.1.2 eslint-visitor-keys: 5.0.1 espree: 11.2.0 @@ -6042,13 +6014,13 @@ snapshots: transitivePeerDependencies: - supports-color - vue-i18n@11.4.0(vue@3.5.33(typescript@5.9.3)): + vue-i18n@11.4.2(vue@3.5.34(typescript@5.9.3)): dependencies: - '@intlify/core-base': 11.4.0 - '@intlify/devtools-types': 11.4.0 - '@intlify/shared': 11.4.0 + '@intlify/core-base': 11.4.2 + '@intlify/devtools-types': 11.4.2 + '@intlify/shared': 11.4.2 '@vue/devtools-api': 6.6.4 - vue: 3.5.33(typescript@5.9.3) + vue: 3.5.34(typescript@5.9.3) vue-lazyload@3.0.0: {} @@ -6056,11 +6028,11 @@ snapshots: dependencies: epubjs: 0.3.93 - vue-router@5.0.6(@vue/compiler-sfc@3.5.33)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.33(typescript@5.9.3)))(vue@3.5.33(typescript@5.9.3)): + vue-router@5.0.7(@vue/compiler-sfc@3.5.34)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)))(vue@3.5.34(typescript@5.9.3)): dependencies: - '@babel/generator': 7.29.1 - '@vue-macros/common': 3.1.2(vue@3.5.33(typescript@5.9.3)) - '@vue/devtools-api': 8.1.1 + '@babel/generator': 8.0.0-rc.5 + '@vue-macros/common': 3.1.2(vue@3.5.34(typescript@5.9.3)) + '@vue/devtools-api': 8.1.2 ast-walker-scope: 0.8.3 chokidar: 5.0.0 json5: 2.2.3 @@ -6074,29 +6046,29 @@ snapshots: tinyglobby: 0.2.16 unplugin: 3.0.0 unplugin-utils: 0.3.1 - vue: 3.5.33(typescript@5.9.3) - yaml: 2.8.3 + vue: 3.5.34(typescript@5.9.3) + yaml: 2.9.0 optionalDependencies: - '@vue/compiler-sfc': 3.5.33 - pinia: 3.0.4(typescript@5.9.3)(vue@3.5.33(typescript@5.9.3)) + '@vue/compiler-sfc': 3.5.34 + pinia: 3.0.4(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)) - vue-toastification@2.0.0-rc.5(vue@3.5.33(typescript@5.9.3)): + vue-toastification@2.0.0-rc.5(vue@3.5.34(typescript@5.9.3)): dependencies: - vue: 3.5.33(typescript@5.9.3) + vue: 3.5.34(typescript@5.9.3) - vue-tsc@3.2.8(typescript@5.9.3): + vue-tsc@3.2.9(typescript@5.9.3): dependencies: '@volar/typescript': 2.4.28 - '@vue/language-core': 3.2.8 + '@vue/language-core': 3.2.9 typescript: 5.9.3 - vue@3.5.33(typescript@5.9.3): + vue@3.5.34(typescript@5.9.3): dependencies: - '@vue/compiler-dom': 3.5.33 - '@vue/compiler-sfc': 3.5.33 - '@vue/runtime-dom': 3.5.33 - '@vue/server-renderer': 3.5.33(vue@3.5.33(typescript@5.9.3)) - '@vue/shared': 3.5.33 + '@vue/compiler-dom': 3.5.34 + '@vue/compiler-sfc': 3.5.34 + '@vue/runtime-dom': 3.5.34 + '@vue/server-renderer': 3.5.34(vue@3.5.34(typescript@5.9.3)) + '@vue/shared': 3.5.34 optionalDependencies: typescript: 5.9.3 @@ -6120,8 +6092,8 @@ snapshots: yaml-eslint-parser@1.3.2: dependencies: eslint-visitor-keys: 3.4.3 - yaml: 2.8.3 + yaml: 2.9.0 - yaml@2.8.3: {} + yaml@2.9.0: {} yocto-queue@0.1.0: {} diff --git a/go.mod b/go.mod index 4442b5648b..1d3203cd2a 100644 --- a/go.mod +++ b/go.mod @@ -26,9 +26,9 @@ require ( github.com/stretchr/testify v1.11.1 github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce go.etcd.io/bbolt v1.4.3 - golang.org/x/crypto v0.50.0 - golang.org/x/image v0.39.0 - golang.org/x/text v0.36.0 + golang.org/x/crypto v0.51.0 + golang.org/x/image v0.40.0 + golang.org/x/text v0.37.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -76,9 +76,9 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/net v0.52.0 // indirect + golang.org/x/net v0.53.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.43.0 // indirect + golang.org/x/sys v0.44.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index d756184d1e..d1f5c801d7 100644 --- a/go.sum +++ b/go.sum @@ -281,8 +281,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= +golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -294,8 +294,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww= -golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA= +golang.org/x/image v0.40.0 h1:Tw4GyDXMo+daZN1znreBRC3VayR1aLFUyUEOLUdW1a8= +golang.org/x/image v0.40.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -334,8 +334,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -375,8 +375,8 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -387,8 +387,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= -golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From da2dff0933e7f8795ba39fe2bad88d0e0dab66f4 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 14:59:45 +0200 Subject: [PATCH 02/53] chore: sync translations --- frontend/src/i18n/de.json | 2 ++ frontend/src/i18n/fr.json | 4 ++-- frontend/src/i18n/hr.json | 2 ++ frontend/src/i18n/ko.json | 2 ++ frontend/src/i18n/lv.json | 2 ++ frontend/src/i18n/nl.json | 2 ++ frontend/src/i18n/pl.json | 2 ++ frontend/src/i18n/pt-pt.json | 2 ++ frontend/src/i18n/pt.json | 2 ++ frontend/src/i18n/ru.json | 2 ++ frontend/src/i18n/zh-cn.json | 2 ++ 11 files changed, 22 insertions(+), 2 deletions(-) diff --git a/frontend/src/i18n/de.json b/frontend/src/i18n/de.json index 02282bdf54..91182022a4 100644 --- a/frontend/src/i18n/de.json +++ b/frontend/src/i18n/de.json @@ -29,6 +29,8 @@ "rename": "Umbenennen", "replace": "Ersetzen", "reportIssue": "Problem melden", + "resumeTransfer": "Resume previous transfert", + "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Speichern", "schedule": "Planen", "search": "Suchen", diff --git a/frontend/src/i18n/fr.json b/frontend/src/i18n/fr.json index af7eac63d5..a92982123d 100644 --- a/frontend/src/i18n/fr.json +++ b/frontend/src/i18n/fr.json @@ -29,8 +29,8 @@ "rename": "Renommer", "replace": "Remplacer", "reportIssue": "Signaler un problème", - "resumeTransfer": "Reprendre un transfert précedement interrompu", - "resumeTransferTooltip": "Ignorer tous les fichiers en conflit, sauf ceux qui sont plus petits sur le serveur, car nous supposons que leur transfert a été interrompu.", + "resumeTransfer": "Resume previous transfert", + "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Enregistrer", "schedule": "Planifier", "search": "Rechercher", diff --git a/frontend/src/i18n/hr.json b/frontend/src/i18n/hr.json index bf4006e909..4978f89f15 100644 --- a/frontend/src/i18n/hr.json +++ b/frontend/src/i18n/hr.json @@ -29,6 +29,8 @@ "rename": "Preimenuj", "replace": "Zamijeni", "reportIssue": "Prijavi grešku", + "resumeTransfer": "Resume previous transfert", + "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Spremi", "schedule": "Zakaži", "search": "Pretraži", diff --git a/frontend/src/i18n/ko.json b/frontend/src/i18n/ko.json index 4d7fb367f9..7ef54241db 100644 --- a/frontend/src/i18n/ko.json +++ b/frontend/src/i18n/ko.json @@ -29,6 +29,8 @@ "rename": "이름 바꾸기", "replace": "대체", "reportIssue": "문제 보고", + "resumeTransfer": "Resume previous transfert", + "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "저장", "schedule": "일정", "search": "검색", diff --git a/frontend/src/i18n/lv.json b/frontend/src/i18n/lv.json index 3bf12027b1..64f353b40a 100644 --- a/frontend/src/i18n/lv.json +++ b/frontend/src/i18n/lv.json @@ -29,6 +29,8 @@ "rename": "Pārdēvēt", "replace": "Aizstāt", "reportIssue": "Ziņot par problēmu", + "resumeTransfer": "Resume previous transfert", + "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Saglabāt", "schedule": "Grafiks", "search": "Meklēt", diff --git a/frontend/src/i18n/nl.json b/frontend/src/i18n/nl.json index c2db058d01..f8877593d5 100644 --- a/frontend/src/i18n/nl.json +++ b/frontend/src/i18n/nl.json @@ -29,6 +29,8 @@ "rename": "Hernoemen", "replace": "Vervangen", "reportIssue": "Probleem melden", + "resumeTransfer": "Vorige overdracht hervatten", + "resumeTransferTooltip": "Alle conflicterende bestanden overslaan, behalve de bestanden die kleiner zijn op de server, omdat we veronderstellen dat hun overdracht is onderbroken.", "save": "Opslaan", "schedule": "Plannen", "search": "Zoeken", diff --git a/frontend/src/i18n/pl.json b/frontend/src/i18n/pl.json index b877689242..84668f63cd 100644 --- a/frontend/src/i18n/pl.json +++ b/frontend/src/i18n/pl.json @@ -29,6 +29,8 @@ "rename": "Zmień nazwę", "replace": "Zamień", "reportIssue": "Zgłoś problem", + "resumeTransfer": "Wznów poprzedni transfer", + "resumeTransferTooltip": "Pomiń wszystkie pliki powodujące konflikty, z wyjątkiem tych, które są mniejsze na serwerze, ponieważ podejrzewamy, że ich transfer został przerwany.", "save": "Zapisz", "schedule": "Harmonogram", "search": "Szukaj", diff --git a/frontend/src/i18n/pt-pt.json b/frontend/src/i18n/pt-pt.json index 30b03f7f7d..fc331e3463 100644 --- a/frontend/src/i18n/pt-pt.json +++ b/frontend/src/i18n/pt-pt.json @@ -29,6 +29,8 @@ "rename": "Mudar o nome", "replace": "Substituir", "reportIssue": "Reportar problema", + "resumeTransfer": "Resume previous transfert", + "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Guardar", "schedule": "Agendar", "search": "Pesquisar", diff --git a/frontend/src/i18n/pt.json b/frontend/src/i18n/pt.json index e910d71ae1..c67b14aec5 100644 --- a/frontend/src/i18n/pt.json +++ b/frontend/src/i18n/pt.json @@ -29,6 +29,8 @@ "rename": "Alterar nome", "replace": "Substituir", "reportIssue": "Reportar erro", + "resumeTransfer": "Resume previous transfert", + "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Guardar", "schedule": "Agendar", "search": "Pesquisar", diff --git a/frontend/src/i18n/ru.json b/frontend/src/i18n/ru.json index 54b666f366..2b769eae9e 100644 --- a/frontend/src/i18n/ru.json +++ b/frontend/src/i18n/ru.json @@ -29,6 +29,8 @@ "rename": "Переименовать", "replace": "Перезаписать", "reportIssue": "Сообщить о проблеме", + "resumeTransfer": "Resume previous transfert", + "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Сохранить", "schedule": "Планировка", "search": "Поиск", diff --git a/frontend/src/i18n/zh-cn.json b/frontend/src/i18n/zh-cn.json index 5e71f308c4..112ac424a1 100644 --- a/frontend/src/i18n/zh-cn.json +++ b/frontend/src/i18n/zh-cn.json @@ -29,6 +29,8 @@ "rename": "重命名", "replace": "替换", "reportIssue": "报告问题", + "resumeTransfer": "Resume previous transfert", + "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "保存", "schedule": "计划", "search": "搜索", From e38c28273aa85329fd9d796c1dd66fa07289b1ab Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 17 May 2026 15:00:44 +0200 Subject: [PATCH 03/53] chore: fix typo --- frontend/src/i18n/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 2e95889eea..832754fd74 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -29,7 +29,7 @@ "rename": "Rename", "replace": "Replace", "reportIssue": "Report Issue", - "resumeTransfer": "Resume previous transfert", + "resumeTransfer": "Resume previous transfer", "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Save", "schedule": "Schedule", From 9cc18a81e3e1b8bf96795bfbe3d83ced294ecfd7 Mon Sep 17 00:00:00 2001 From: mehmet turac Date: Sun, 17 May 2026 16:01:02 +0300 Subject: [PATCH 04/53] fix: show item shares from all users to admins (#5941) --- http/share.go | 10 ++++- share/storage.go | 17 +++++++++ share/storage_test.go | 85 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 share/storage_test.go diff --git a/http/share.go b/http/share.go index 35125dba40..ec95d06629 100644 --- a/http/share.go +++ b/http/share.go @@ -57,7 +57,15 @@ var shareListHandler = withPermShare(func(w http.ResponseWriter, r *http.Request }) var shareGetsHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { - s, err := d.store.Share.Gets(r.URL.Path, d.user.ID) + var ( + s []*share.Link + err error + ) + if d.user.Perm.Admin { + s, err = d.store.Share.GetsByPath(r.URL.Path) + } else { + s, err = d.store.Share.Gets(r.URL.Path, d.user.ID) + } if errors.Is(err, fberrors.ErrNotExist) { return renderJSON(w, r, []*share.Link{}) } diff --git a/share/storage.go b/share/storage.go index 76ba26eb7a..9c28cfd1c6 100644 --- a/share/storage.go +++ b/share/storage.go @@ -110,6 +110,23 @@ func (s *Storage) Gets(path string, id uint) ([]*Link, error) { return links, nil } +// GetsByPath returns all non-expired links for a path. +func (s *Storage) GetsByPath(path string) ([]*Link, error) { + links, err := s.All() + if err != nil { + return nil, err + } + + filtered := make([]*Link, 0, len(links)) + for _, link := range links { + if link.Path == path { + filtered = append(filtered, link) + } + } + + return filtered, nil +} + // Save wraps a StorageBackend.Save func (s *Storage) Save(l *Link) error { return s.back.Save(l) diff --git a/share/storage_test.go b/share/storage_test.go new file mode 100644 index 0000000000..e402f6e9d2 --- /dev/null +++ b/share/storage_test.go @@ -0,0 +1,85 @@ +package share + +import ( + "reflect" + "testing" +) + +type fakeBackend struct { + links []*Link +} + +func (f fakeBackend) All() ([]*Link, error) { + return f.links, nil +} + +func (f fakeBackend) FindByUserID(id uint) ([]*Link, error) { + var links []*Link + for _, link := range f.links { + if link.UserID == id { + links = append(links, link) + } + } + return links, nil +} + +func (f fakeBackend) GetByHash(hash string) (*Link, error) { + for _, link := range f.links { + if link.Hash == hash { + return link, nil + } + } + return nil, nil +} + +func (f fakeBackend) GetPermanent(path string, id uint) (*Link, error) { + for _, link := range f.links { + if link.Path == path && link.UserID == id && link.Expire == 0 { + return link, nil + } + } + return nil, nil +} + +func (f fakeBackend) Gets(path string, id uint) ([]*Link, error) { + var links []*Link + for _, link := range f.links { + if link.Path == path && link.UserID == id { + links = append(links, link) + } + } + return links, nil +} + +func (f fakeBackend) Save(_ *Link) error { + return nil +} + +func (f fakeBackend) Delete(_ string) error { + return nil +} + +func (f fakeBackend) DeleteWithPathPrefix(_ string) error { + return nil +} + +func TestGetsByPathReturnsLinksFromAllUsers(t *testing.T) { + t.Parallel() + + expected := []*Link{ + {Hash: "a", Path: "/file.txt", UserID: 1}, + {Hash: "b", Path: "/file.txt", UserID: 2}, + } + store := NewStorage(fakeBackend{ + links: append(expected, &Link{Hash: "c", Path: "/other.txt", UserID: 3}), + }) + + links, err := store.GetsByPath("/file.txt") + if err != nil { + t.Fatalf("GetsByPath returned error: %v", err) + } + + if !reflect.DeepEqual(links, expected) { + t.Fatalf("GetsByPath returned %#v, want %#v", links, expected) + } +} From d978d1eccae6127618681333490afa0613e7d2ec Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 15:05:08 +0200 Subject: [PATCH 05/53] chore: sync translations (#5945) --- frontend/src/i18n/de.json | 2 +- frontend/src/i18n/fr.json | 2 +- frontend/src/i18n/hr.json | 2 +- frontend/src/i18n/ko.json | 2 +- frontend/src/i18n/lv.json | 2 +- frontend/src/i18n/nl.json | 2 +- frontend/src/i18n/pl.json | 2 +- frontend/src/i18n/pt-pt.json | 2 +- frontend/src/i18n/pt.json | 2 +- frontend/src/i18n/ru.json | 2 +- frontend/src/i18n/zh-cn.json | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/frontend/src/i18n/de.json b/frontend/src/i18n/de.json index 91182022a4..e2fe01361e 100644 --- a/frontend/src/i18n/de.json +++ b/frontend/src/i18n/de.json @@ -29,7 +29,7 @@ "rename": "Umbenennen", "replace": "Ersetzen", "reportIssue": "Problem melden", - "resumeTransfer": "Resume previous transfert", + "resumeTransfer": "Resume previous transfer", "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Speichern", "schedule": "Planen", diff --git a/frontend/src/i18n/fr.json b/frontend/src/i18n/fr.json index a92982123d..e35ed89d5f 100644 --- a/frontend/src/i18n/fr.json +++ b/frontend/src/i18n/fr.json @@ -29,7 +29,7 @@ "rename": "Renommer", "replace": "Remplacer", "reportIssue": "Signaler un problème", - "resumeTransfer": "Resume previous transfert", + "resumeTransfer": "Resume previous transfer", "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Enregistrer", "schedule": "Planifier", diff --git a/frontend/src/i18n/hr.json b/frontend/src/i18n/hr.json index 4978f89f15..a6732091a0 100644 --- a/frontend/src/i18n/hr.json +++ b/frontend/src/i18n/hr.json @@ -29,7 +29,7 @@ "rename": "Preimenuj", "replace": "Zamijeni", "reportIssue": "Prijavi grešku", - "resumeTransfer": "Resume previous transfert", + "resumeTransfer": "Resume previous transfer", "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Spremi", "schedule": "Zakaži", diff --git a/frontend/src/i18n/ko.json b/frontend/src/i18n/ko.json index 7ef54241db..1accf5f900 100644 --- a/frontend/src/i18n/ko.json +++ b/frontend/src/i18n/ko.json @@ -29,7 +29,7 @@ "rename": "이름 바꾸기", "replace": "대체", "reportIssue": "문제 보고", - "resumeTransfer": "Resume previous transfert", + "resumeTransfer": "Resume previous transfer", "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "저장", "schedule": "일정", diff --git a/frontend/src/i18n/lv.json b/frontend/src/i18n/lv.json index 64f353b40a..75c3a939b7 100644 --- a/frontend/src/i18n/lv.json +++ b/frontend/src/i18n/lv.json @@ -29,7 +29,7 @@ "rename": "Pārdēvēt", "replace": "Aizstāt", "reportIssue": "Ziņot par problēmu", - "resumeTransfer": "Resume previous transfert", + "resumeTransfer": "Resume previous transfer", "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Saglabāt", "schedule": "Grafiks", diff --git a/frontend/src/i18n/nl.json b/frontend/src/i18n/nl.json index f8877593d5..69b0919bbf 100644 --- a/frontend/src/i18n/nl.json +++ b/frontend/src/i18n/nl.json @@ -29,7 +29,7 @@ "rename": "Hernoemen", "replace": "Vervangen", "reportIssue": "Probleem melden", - "resumeTransfer": "Vorige overdracht hervatten", + "resumeTransfer": "Resume previous transfer", "resumeTransferTooltip": "Alle conflicterende bestanden overslaan, behalve de bestanden die kleiner zijn op de server, omdat we veronderstellen dat hun overdracht is onderbroken.", "save": "Opslaan", "schedule": "Plannen", diff --git a/frontend/src/i18n/pl.json b/frontend/src/i18n/pl.json index 84668f63cd..a3cc6c3270 100644 --- a/frontend/src/i18n/pl.json +++ b/frontend/src/i18n/pl.json @@ -29,7 +29,7 @@ "rename": "Zmień nazwę", "replace": "Zamień", "reportIssue": "Zgłoś problem", - "resumeTransfer": "Wznów poprzedni transfer", + "resumeTransfer": "Resume previous transfer", "resumeTransferTooltip": "Pomiń wszystkie pliki powodujące konflikty, z wyjątkiem tych, które są mniejsze na serwerze, ponieważ podejrzewamy, że ich transfer został przerwany.", "save": "Zapisz", "schedule": "Harmonogram", diff --git a/frontend/src/i18n/pt-pt.json b/frontend/src/i18n/pt-pt.json index fc331e3463..2c04e8aa7c 100644 --- a/frontend/src/i18n/pt-pt.json +++ b/frontend/src/i18n/pt-pt.json @@ -29,7 +29,7 @@ "rename": "Mudar o nome", "replace": "Substituir", "reportIssue": "Reportar problema", - "resumeTransfer": "Resume previous transfert", + "resumeTransfer": "Resume previous transfer", "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Guardar", "schedule": "Agendar", diff --git a/frontend/src/i18n/pt.json b/frontend/src/i18n/pt.json index c67b14aec5..d05232511d 100644 --- a/frontend/src/i18n/pt.json +++ b/frontend/src/i18n/pt.json @@ -29,7 +29,7 @@ "rename": "Alterar nome", "replace": "Substituir", "reportIssue": "Reportar erro", - "resumeTransfer": "Resume previous transfert", + "resumeTransfer": "Resume previous transfer", "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Guardar", "schedule": "Agendar", diff --git a/frontend/src/i18n/ru.json b/frontend/src/i18n/ru.json index 2b769eae9e..1cdab2cace 100644 --- a/frontend/src/i18n/ru.json +++ b/frontend/src/i18n/ru.json @@ -29,7 +29,7 @@ "rename": "Переименовать", "replace": "Перезаписать", "reportIssue": "Сообщить о проблеме", - "resumeTransfer": "Resume previous transfert", + "resumeTransfer": "Resume previous transfer", "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "Сохранить", "schedule": "Планировка", diff --git a/frontend/src/i18n/zh-cn.json b/frontend/src/i18n/zh-cn.json index 112ac424a1..4d021be37a 100644 --- a/frontend/src/i18n/zh-cn.json +++ b/frontend/src/i18n/zh-cn.json @@ -29,7 +29,7 @@ "rename": "重命名", "replace": "替换", "reportIssue": "报告问题", - "resumeTransfer": "Resume previous transfert", + "resumeTransfer": "Resume previous transfer", "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", "save": "保存", "schedule": "计划", From a418dd6bb367a609cb38835f9a76e94df995f37e Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 17 May 2026 15:10:16 +0200 Subject: [PATCH 06/53] chore: revert node dependencies updates --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 1d3203cd2a..4442b5648b 100644 --- a/go.mod +++ b/go.mod @@ -26,9 +26,9 @@ require ( github.com/stretchr/testify v1.11.1 github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce go.etcd.io/bbolt v1.4.3 - golang.org/x/crypto v0.51.0 - golang.org/x/image v0.40.0 - golang.org/x/text v0.37.0 + golang.org/x/crypto v0.50.0 + golang.org/x/image v0.39.0 + golang.org/x/text v0.36.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -76,9 +76,9 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/net v0.53.0 // indirect + golang.org/x/net v0.52.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.44.0 // indirect + golang.org/x/sys v0.43.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index d1f5c801d7..d756184d1e 100644 --- a/go.sum +++ b/go.sum @@ -281,8 +281,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= -golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -294,8 +294,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.40.0 h1:Tw4GyDXMo+daZN1znreBRC3VayR1aLFUyUEOLUdW1a8= -golang.org/x/image v0.40.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA= +golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww= +golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -334,8 +334,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= -golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -375,8 +375,8 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -387,8 +387,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 22b848f26ef8ae889eef132232dbd0de347e0efd Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 17 May 2026 15:11:55 +0200 Subject: [PATCH 07/53] chore(release): 2.63.4 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c26ac0271..c19db90812 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.63.4](https://github.com/filebrowser/filebrowser/compare/v2.63.3...v2.63.4) (2026-05-17) + + +### Bug Fixes + +* show item shares from all users to admins ([#5941](https://github.com/filebrowser/filebrowser/issues/5941)) ([9cc18a8](https://github.com/filebrowser/filebrowser/commit/9cc18a81e3e1b8bf96795bfbe3d83ced294ecfd7)) + ## [2.63.3](https://github.com/filebrowser/filebrowser/compare/v2.63.2...v2.63.3) (2026-05-05) From 6ad8160aa3309314c1b471c5090b67c824464396 Mon Sep 17 00:00:00 2001 From: Jose Olcese <3219544+jolcese@users.noreply.github.com> Date: Thu, 21 May 2026 06:39:45 -0700 Subject: [PATCH 08/53] fix(router): handle undefined catchAll param on root redirect (#5955) --- frontend/src/router/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 60e7b6360e..a39482e1c9 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -145,8 +145,11 @@ const routes = [ }, { path: "/:catchAll(.*)*", - redirect: (to: RouteLocation) => - `/files/${[...to.params.catchAll].join("/")}`, + redirect: (to: RouteLocation) => { + const catchAll = to.params.catchAll; + if (!catchAll) return "/files/"; + return `/files/${Array.isArray(catchAll) ? catchAll.join("/") : catchAll}`; + }, }, ]; From a1e442ef9e4a14719184bf02c50dbc981ecf8665 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 21 May 2026 15:42:07 +0200 Subject: [PATCH 09/53] chore(release): 2.63.5 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c19db90812..87ff5e54a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.63.5](https://github.com/filebrowser/filebrowser/compare/v2.63.4...v2.63.5) (2026-05-21) + + +### Bug Fixes + +* **router:** handle undefined catchAll param on root redirect ([#5955](https://github.com/filebrowser/filebrowser/issues/5955)) ([6ad8160](https://github.com/filebrowser/filebrowser/commit/6ad8160aa3309314c1b471c5090b67c824464396)) + ## [2.63.4](https://github.com/filebrowser/filebrowser/compare/v2.63.3...v2.63.4) (2026-05-17) From ca0108f0709741828c5d4c9f0406e2b25dd7ca88 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 21 May 2026 16:52:04 +0200 Subject: [PATCH 10/53] chore: disable automatic major updates --- renovate.json | 1 + 1 file changed, 1 insertion(+) diff --git a/renovate.json b/renovate.json index ae7193e85a..bcdca14a2f 100644 --- a/renovate.json +++ b/renovate.json @@ -5,6 +5,7 @@ "group:allNonMajor", "group:allDigest", ":disableDependencyDashboard", + ":disableMajorUpdates", ":semanticCommitTypeAll(chore)" ], "postUpdateOptions": [ From 34ae34e764d72540c039f1f5ea2ec4c974168c1f Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 09:59:36 +0200 Subject: [PATCH 11/53] fix: remove undocumented hook auth with shell replacement --- auth/hook.go | 16 --------- auth/hook_test.go | 88 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 16 deletions(-) create mode 100644 auth/hook_test.go diff --git a/auth/hook.go b/auth/hook.go index 2541b684b9..60c75461c7 100644 --- a/auth/hook.go +++ b/auth/hook.go @@ -86,22 +86,6 @@ func (a *HookAuth) LoginPage() bool { // RunCommand starts the hook command and returns the action func (a *HookAuth) RunCommand() (string, error) { command := strings.Split(a.Command, " ") - envMapping := func(key string) string { - switch key { - case "USERNAME": - return a.Cred.Username - case "PASSWORD": - return a.Cred.Password - default: - return os.Getenv(key) - } - } - for i, arg := range command { - if i == 0 { - continue - } - command[i] = os.Expand(arg, envMapping) - } cmd := exec.Command(command[0], command[1:]...) cmd.Env = append(os.Environ(), fmt.Sprintf("USERNAME=%s", a.Cred.Username)) diff --git a/auth/hook_test.go b/auth/hook_test.go new file mode 100644 index 0000000000..4b0112b1b4 --- /dev/null +++ b/auth/hook_test.go @@ -0,0 +1,88 @@ +package auth + +import ( + "os" + "path/filepath" + "runtime" + "testing" +) + +// writeHookScript writes a POSIX shell script to a temp file and returns its +// path, marking it executable. +func writeHookScript(t *testing.T, body string) string { + t.Helper() + path := filepath.Join(t.TempDir(), "hook.sh") + if err := os.WriteFile(path, []byte("#!/bin/sh\n"+body), 0o700); err != nil { + t.Fatalf("failed to write hook script: %v", err) + } + return path +} + +// TestRunCommandNoCredentialInjection ensures that attacker-controlled +// credentials submitted at the unauthenticated login endpoint cannot be +// injected into the hook command string. Credentials must only ever reach the +// hook through the USERNAME/PASSWORD environment variables, never via string +// substitution into the command itself (CWE-78/CWE-88). +func TestRunCommandNoCredentialInjection(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("uses POSIX shell") + } + + marker := filepath.Join(t.TempDir(), "pwned") + + // The hook simply blocks. If the credential were ever interpolated into the + // command string and evaluated by a shell, the embedded `touch` would + // create the marker file. + script := writeHookScript(t, "echo hook.action=block\n") + + a := &HookAuth{ + Command: script, + Cred: hookCred{ + Username: `"; touch ` + marker + `; #`, + Password: `$(touch ` + marker + `)`, + }, + } + + action, err := a.RunCommand() + if err != nil { + t.Fatalf("RunCommand returned error: %v", err) + } + if action != "block" { + t.Fatalf("expected action %q, got %q", "block", action) + } + if _, err := os.Stat(marker); err == nil { + t.Fatalf("credential injection executed: marker file %q was created", marker) + } +} + +// TestRunCommandReceivesCredentialsViaEnv verifies the supported contract: the +// hook receives credentials through the USERNAME and PASSWORD environment +// variables. +func TestRunCommandReceivesCredentialsViaEnv(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("uses POSIX shell") + } + + script := writeHookScript(t, `if [ "$USERNAME" = alice ] && [ "$PASSWORD" = secret ]; then + echo hook.action=auth +else + echo hook.action=block +fi +`) + + a := &HookAuth{ + Command: script, + Cred: hookCred{ + Username: "alice", + Password: "secret", + }, + } + + action, err := a.RunCommand() + if err != nil { + t.Fatalf("RunCommand returned error: %v", err) + } + if action != "auth" { + t.Fatalf("expected action %q, got %q", "auth", action) + } +} From 0d3eb9bea96127e6d7b53a84f4551d709affe865 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 10:20:12 +0200 Subject: [PATCH 12/53] docs: clarify hide dotfiles --- cmd/users.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/users.go b/cmd/users.go index 7aae00a995..639c2ef383 100644 --- a/cmd/users.go +++ b/cmd/users.go @@ -80,7 +80,7 @@ func addUserFlags(flags *pflag.FlagSet) { flags.Bool("singleClick", false, "use single clicks only") flags.Bool("redirectAfterCopyMove", false, "redirect to destination after copy/move") flags.Bool("dateFormat", false, "use date format (true for absolute time, false for relative)") - flags.Bool("hideDotfiles", false, "hide dotfiles") + flags.Bool("hideDotfiles", false, "hide dotfiles in file listings") flags.String("aceEditorTheme", "", "ace editor's syntax highlighting theme for users") } From e07c59df0b850f5924d5b1683e8609661ddcf534 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 10:24:05 +0200 Subject: [PATCH 13/53] fix: incorrect access control in public directory shares via rule path rebasing --- http/data.go | 16 +++++++ http/public.go | 5 +++ http/public_test.go | 105 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+) diff --git a/http/data.go b/http/data.go index d591053e05..37ad5ef098 100644 --- a/http/data.go +++ b/http/data.go @@ -3,6 +3,7 @@ package fbhttp import ( "log" "net/http" + gopath "path" "strconv" "github.com/tomasen/realip" @@ -23,10 +24,25 @@ type data struct { store *storage.Storage user *users.User raw interface{} + + // checkerPrefix is prepended to every path before evaluating rules. It is + // set when the user's filesystem has been rebased onto a subdirectory (as + // done for public shares), so that rules — which are relative to the user's + // original scope — are still matched against the real path instead of the + // rebased one. Empty for regular requests. + checkerPrefix string } // Check implements rules.Checker. func (d *data) Check(path string) bool { + // When the filesystem has been rebased (e.g. a public share rooted at a + // subdirectory), the incoming path is relative to that root. Resolve it + // back to the user's original scope before matching rules, otherwise rules + // targeting paths below the share root would be silently bypassed. + if d.checkerPrefix != "" { + path = gopath.Join(d.checkerPrefix, path) + } + if d.user.HideDotfiles && rules.MatchHidden(path) { return false } diff --git a/http/public.go b/http/public.go index 29679be85d..bf49927c71 100644 --- a/http/public.go +++ b/http/public.go @@ -67,6 +67,11 @@ var withHashFile = func(fn handleFunc) handleFunc { // set fs root to the shared file/folder d.user.Fs = afero.NewBasePathFs(d.user.Fs, basePath) + // the filesystem is now rebased onto basePath, so paths handed to the + // rule checker are relative to it. Resolve them back to the user's + // original scope so deny rules below the share root keep applying. + d.checkerPrefix = basePath + file, err = files.NewFileInfo(&files.FileOptions{ Fs: d.user.Fs, Path: filePath, diff --git a/http/public_test.go b/http/public_test.go index ea38ce51af..bc57a9cac7 100644 --- a/http/public_test.go +++ b/http/public_test.go @@ -10,6 +10,7 @@ import ( "github.com/asdine/storm/v3" "github.com/spf13/afero" + "github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/share" "github.com/filebrowser/filebrowser/v2/storage/bolt" @@ -143,6 +144,110 @@ func TestPublicShareHandlerAuthentication(t *testing.T) { } } +// TestPublicShareHandlerRules ensures that owner rules keep applying to paths +// below a shared directory, even though the share rebases the filesystem onto +// that directory. A deny rule relative to the owner's scope must not be +// bypassable by requesting the blocked path through the public share. +func TestPublicShareHandlerRules(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + handler handleFunc + path string + expectedStatusCode int + }{ + "blocked file via dl handler, 403": { + handler: publicDlHandler, + path: "h/private/secret.txt", + expectedStatusCode: 403, + }, + "blocked dir listing via share handler, 403": { + handler: publicShareHandler, + path: "h/private/", + expectedStatusCode: 403, + }, + "blocked dir download via dl handler, 403": { + handler: publicDlHandler, + path: "h/private/", + expectedStatusCode: 403, + }, + "allowed file via dl handler, 200": { + handler: publicDlHandler, + path: "h/public/readme.txt", + expectedStatusCode: 200, + }, + "allowed dir listing via share handler, 200": { + handler: publicShareHandler, + path: "h/public/", + expectedStatusCode: 200, + }, + } + + for name, tc := range testCases { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + t.Parallel() + + dbPath := filepath.Join(t.TempDir(), "db") + db, err := storm.Open(dbPath) + if err != nil { + t.Fatalf("failed to open db: %v", err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Errorf("failed to close db: %v", err) + } + }) + + storage, err := bolt.NewStorage(db) + if err != nil { + t.Fatalf("failed to get storage: %v", err) + } + if err := storage.Share.Save(&share.Link{Hash: "h", UserID: 1, Path: "/projects"}); err != nil { + t.Fatalf("failed to save share: %v", err) + } + if err := storage.Users.Save(&users.User{ + Username: "username", + Password: "pw", + Perm: users.Permissions{Share: true, Download: true}, + Rules: []rules.Rule{ + {Allow: false, Path: "/projects/private"}, + }, + }); err != nil { + t.Fatalf("failed to save user: %v", err) + } + if err := storage.Settings.Save(&settings.Settings{Key: []byte("key")}); err != nil { + t.Fatalf("failed to save settings: %v", err) + } + + fs := afero.NewMemMapFs() + if err := afero.WriteFile(fs, "/projects/private/secret.txt", []byte("top secret"), 0o600); err != nil { + t.Fatalf("failed to write secret file: %v", err) + } + if err := afero.WriteFile(fs, "/projects/public/readme.txt", []byte("hello"), 0o600); err != nil { + t.Fatalf("failed to write public file: %v", err) + } + + storage.Users = &customFSUser{ + Store: storage.Users, + fs: fs, + } + + req := newHTTPRequest(t, func(r *http.Request) { r.URL.Path = tc.path }) + + recorder := httptest.NewRecorder() + handler := handle(tc.handler, "", storage, &settings.Server{}) + + handler.ServeHTTP(recorder, req) + result := recorder.Result() + defer result.Body.Close() + if result.StatusCode != tc.expectedStatusCode { + t.Errorf("expected status code %d, got status code %d", tc.expectedStatusCode, result.StatusCode) + } + }) + } +} + func newHTTPRequest(t *testing.T, requestModifiers ...func(*http.Request)) *http.Request { t.Helper() r, err := http.NewRequest(http.MethodGet, "h", http.NoBody) From 0231b7ebdfbe77a6c54027d30c4856c3fd81ee4d Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 10:31:44 +0200 Subject: [PATCH 14/53] fix: cross-user unauthorized share-link deletion --- http/resource.go | 2 +- share/storage.go | 6 +-- share/storage_test.go | 2 +- storage/bolt/share.go | 16 ++++++- storage/bolt/share_test.go | 97 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 storage/bolt/share_test.go diff --git a/http/resource.go b/http/resource.go index 853241149e..e5c519ead4 100644 --- a/http/resource.go +++ b/http/resource.go @@ -100,7 +100,7 @@ func resourceDeleteHandler(fileCache FileCache) handleFunc { return errToStatus(err), err } - err = d.store.Share.DeleteWithPathPrefix(file.Path) + err = d.store.Share.DeleteWithPathPrefix(file.Path, d.user.ID) if err != nil { log.Printf("WARNING: Error(s) occurred while deleting associated shares with file: %s", err) } diff --git a/share/storage.go b/share/storage.go index 9c28cfd1c6..684ab018e1 100644 --- a/share/storage.go +++ b/share/storage.go @@ -15,7 +15,7 @@ type StorageBackend interface { Gets(path string, id uint) ([]*Link, error) Save(s *Link) error Delete(hash string) error - DeleteWithPathPrefix(path string) error + DeleteWithPathPrefix(path string, userID uint) error } // Storage is a storage. @@ -137,6 +137,6 @@ func (s *Storage) Delete(hash string) error { return s.back.Delete(hash) } -func (s *Storage) DeleteWithPathPrefix(path string) error { - return s.back.DeleteWithPathPrefix(path) +func (s *Storage) DeleteWithPathPrefix(path string, userID uint) error { + return s.back.DeleteWithPathPrefix(path, userID) } diff --git a/share/storage_test.go b/share/storage_test.go index e402f6e9d2..dd78fe717f 100644 --- a/share/storage_test.go +++ b/share/storage_test.go @@ -59,7 +59,7 @@ func (f fakeBackend) Delete(_ string) error { return nil } -func (f fakeBackend) DeleteWithPathPrefix(_ string) error { +func (f fakeBackend) DeleteWithPathPrefix(_ string, _ uint) error { return nil } diff --git a/storage/bolt/share.go b/storage/bolt/share.go index c5b9148301..621ea6f15f 100644 --- a/storage/bolt/share.go +++ b/storage/bolt/share.go @@ -2,6 +2,7 @@ package bolt import ( "errors" + "strings" "github.com/asdine/storm/v3" "github.com/asdine/storm/v3/q" @@ -76,14 +77,27 @@ func (s shareBackend) Delete(hash string) error { return err } -func (s shareBackend) DeleteWithPathPrefix(pathPrefix string) error { +func (s shareBackend) DeleteWithPathPrefix(pathPrefix string, userID uint) error { var links []share.Link if err := s.db.Prefix("Path", pathPrefix, &links); err != nil { + if errors.Is(err, storm.ErrNotFound) { + return nil + } return err } + prefix := strings.TrimRight(pathPrefix, "/") + var err error for _, link := range links { + if link.UserID != userID { + continue + } + + if link.Path != prefix && !strings.HasPrefix(link.Path, prefix+"/") { + continue + } + err = errors.Join(err, s.db.DeleteStruct(&share.Link{Hash: link.Hash})) } return err diff --git a/storage/bolt/share_test.go b/storage/bolt/share_test.go new file mode 100644 index 0000000000..e303cec696 --- /dev/null +++ b/storage/bolt/share_test.go @@ -0,0 +1,97 @@ +package bolt + +import ( + "os" + "sort" + "testing" + + "github.com/asdine/storm/v3" + + "github.com/filebrowser/filebrowser/v2/share" +) + +func newTestShareBackend(t *testing.T) shareBackend { + t.Helper() + + f, err := os.CreateTemp(t.TempDir(), "shares-*.db") + if err != nil { + t.Fatalf("failed to create temp db: %v", err) + } + _ = f.Close() + + db, err := storm.Open(f.Name()) + if err != nil { + t.Fatalf("failed to open db: %v", err) + } + t.Cleanup(func() { _ = db.Close() }) + + return shareBackend{db: db} +} + +func remainingHashes(t *testing.T, s shareBackend) []string { + t.Helper() + + links, err := s.All() + if err != nil { + t.Fatalf("All returned error: %v", err) + } + + hashes := make([]string, 0, len(links)) + for _, link := range links { + hashes = append(hashes, link.Hash) + } + sort.Strings(hashes) + return hashes +} + +func TestDeleteWithPathPrefix(t *testing.T) { + t.Parallel() + + s := newTestShareBackend(t) + + links := []*share.Link{ + // user 1's links + {Hash: "u1-a", Path: "/a", UserID: 1}, + {Hash: "u1-a-child", Path: "/a/child.txt", UserID: 1}, + {Hash: "u1-abc", Path: "/abc", UserID: 1}, // not a descendant of /a + {Hash: "u1-other", Path: "/other", UserID: 1}, + // user 2's links — must never be touched when user 1 deletes + {Hash: "u2-a", Path: "/a", UserID: 2}, + {Hash: "u2-a-child", Path: "/a/child.txt", UserID: 2}, + } + for _, l := range links { + if err := s.Save(l); err != nil { + t.Fatalf("failed to save link %s: %v", l.Hash, err) + } + } + + // User 1 deletes their directory /a. Only user 1's /a and its descendants + // should be removed; /abc (sibling sharing a byte prefix) and all of user + // 2's links must remain. + if err := s.DeleteWithPathPrefix("/a", 1); err != nil { + t.Fatalf("DeleteWithPathPrefix returned error: %v", err) + } + + got := remainingHashes(t, s) + want := []string{"u1-abc", "u1-other", "u2-a", "u2-a-child"} + if len(got) != len(want) { + t.Fatalf("remaining hashes = %v, want %v", got, want) + } + for i := range want { + if got[i] != want[i] { + t.Fatalf("remaining hashes = %v, want %v", got, want) + } + } +} + +func TestDeleteWithPathPrefixNoMatch(t *testing.T) { + t.Parallel() + + s := newTestShareBackend(t) + + // No links exist at all: the storm Prefix query returns ErrNotFound, which + // must be treated as a no-op rather than surfaced as an error. + if err := s.DeleteWithPathPrefix("/a", 1); err != nil { + t.Fatalf("DeleteWithPathPrefix on empty store returned error: %v", err) + } +} From 847d08bdd135e5c3659f2e6dea2f0cd36617af9b Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 11:20:31 +0200 Subject: [PATCH 15/53] fix: address three security disclosures (archive traversal, login DoS, symlink escape) - http/raw.go: strip Windows backslash separators from archive entry names on any host. filepath.ToSlash is a no-op for "\" on Linux, so a stored backslash filename was emitted verbatim and could escape the extraction directory on Windows extractors (zip-slip). (GHSA-gxjx-7m74-hcq8) - http/auth.go: cap the login and signup request bodies with http.MaxBytesReader (1 MiB). The JSON decoder previously read an arbitrarily large password into memory before bcrypt truncated it, enabling unauthenticated memory-exhaustion DoS. (GHSA-w5fm-68j4-fpc4) - files/file.go, http/resource.go: add files.WithinScope and refuse to follow a symlink whose on-disk target escapes the user's scoped root, on both the read path (stat) and the write path (writeFile). Prevents a scoped user from reading/overwriting/sharing files outside their scope via a pre-existing escaping symlink. (GHSA-239w-m3h6-ch8v) Co-Authored-By: Claude Opus 4.8 --- files/file.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ http/auth.go | 10 ++++++++- http/raw.go | 6 ++++++ http/resource.go | 7 ++++++ 4 files changed, 77 insertions(+), 1 deletion(-) diff --git a/files/file.go b/files/file.go index 2ba432dc5e..3b38f31459 100644 --- a/files/file.go +++ b/files/file.go @@ -133,6 +133,17 @@ func stat(opts *FileOptions) (*FileInfo, error) { return file, nil } + // The path is a symlink. Refuse to follow it if its on-disk target escapes + // the user's scoped root; otherwise a symlink that lives lexically inside + // the scope but points outside it would let a restricted user read, write, + // or share files beyond their boundary. + if file != nil && file.IsSymlink { + ok, scopeErr := WithinScope(opts.Fs, opts.Path) + if scopeErr != nil || !ok { + return nil, os.ErrPermission + } + } + // fs doesn't support afero.Lstater interface or the file is a symlink info, err := opts.Fs.Stat(opts.Path) if err != nil { @@ -165,6 +176,50 @@ func stat(opts *FileOptions) (*FileInfo, error) { return file, nil } +// WithinScope reports whether the on-disk target of p — after resolving any +// symbolic links — stays within the scoped root of fsys. It exists to stop a +// symlink that lives lexically inside a user's scope but points outside it +// from being followed for reads, writes, or shares. +// +// Paths that do not exist yet (e.g. a brand-new file being created) are +// validated against their nearest existing ancestor, so legitimate new files +// are always allowed. For a filesystem that is not scoped with BasePathFs the +// check is a no-op and returns true. +// +// Note: a dangling symlink whose target does not yet exist resolves to its +// containing directory and is therefore allowed; writing through such a link +// could still create a file outside the scope. Callers that create files +// should treat this as best-effort and rely on rejecting existing escaping +// symlinks, which covers the disclosure and overwrite vectors. +func WithinScope(fsys afero.Fs, p string) (bool, error) { + bfs, ok := fsys.(*afero.BasePathFs) + if !ok { + // Not a scoped filesystem; nothing to enforce. + return true, nil + } + + root, err := filepath.EvalSymlinks(afero.FullBaseFsPath(bfs, "/")) + if err != nil { + return false, err + } + + target := afero.FullBaseFsPath(bfs, p) + resolved, err := filepath.EvalSymlinks(target) + for errors.Is(err, fs.ErrNotExist) { + parent := filepath.Dir(target) + if parent == target { + break + } + target = parent + resolved, err = filepath.EvalSymlinks(target) + } + if err != nil { + return false, err + } + + return resolved == root || strings.HasPrefix(resolved, root+string(filepath.Separator)), nil +} + // Checksum checksums a given File for a given User, using a specific // algorithm. The checksums data is saved on File object. func (i *FileInfo) Checksum(algo string) error { diff --git a/http/auth.go b/http/auth.go index 59f15a36a0..4381e86c97 100644 --- a/http/auth.go +++ b/http/auth.go @@ -20,6 +20,8 @@ import ( const ( DefaultTokenExpirationTime = time.Hour * 2 + + maxAuthBodySize = 1 << 20 // 1 MiB ) type userInfo struct { @@ -120,6 +122,10 @@ func withAdmin(fn handleFunc) handleFunc { func loginHandler(tokenExpireTime time.Duration) handleFunc { return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { + if r.Body != nil { + r.Body = http.MaxBytesReader(w, r.Body, maxAuthBodySize) + } + auther, err := d.store.Auth.Get(d.settings.AuthMethod) if err != nil { return http.StatusInternalServerError, err @@ -142,7 +148,7 @@ type signupBody struct { Password string `json:"password"` } -var signupHandler = func(_ http.ResponseWriter, r *http.Request, d *data) (int, error) { +var signupHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { if !d.settings.Signup { return http.StatusMethodNotAllowed, nil } @@ -151,6 +157,8 @@ var signupHandler = func(_ http.ResponseWriter, r *http.Request, d *data) (int, return http.StatusBadRequest, nil } + r.Body = http.MaxBytesReader(w, r.Body, maxAuthBodySize) + info := &signupBody{} err := json.NewDecoder(r.Body).Decode(info) if err != nil { diff --git a/http/raw.go b/http/raw.go index b5c86643e5..35e474ae21 100644 --- a/http/raw.go +++ b/http/raw.go @@ -125,6 +125,12 @@ func getFiles(d *data, path, commonPath string) ([]archives.FileInfo, error) { nameInArchive := strings.TrimPrefix(path, commonPath) nameInArchive = strings.TrimPrefix(nameInArchive, string(filepath.Separator)) nameInArchive = filepath.ToSlash(nameInArchive) + // filepath.ToSlash only rewrites the host separator, so on a Linux + // host a stored backslash survives and is emitted verbatim into the + // archive. Windows extractors then treat "\" as a path separator, + // allowing the entry to escape the extraction directory (zip-slip). + // Strip Windows separators regardless of host OS. + nameInArchive = strings.ReplaceAll(nameInArchive, "\\", "/") archiveFiles = append(archiveFiles, archives.FileInfo{ FileInfo: info, diff --git a/http/resource.go b/http/resource.go index e5c519ead4..9f7bfb0176 100644 --- a/http/resource.go +++ b/http/resource.go @@ -295,6 +295,13 @@ func addVersionSuffix(source string, afs afero.Fs) string { } func writeFile(afs afero.Fs, dst string, in io.Reader, fileMode, dirMode fs.FileMode) (os.FileInfo, error) { + // Refuse to write through a symlink that escapes the user's scope, so an + // overwrite of an existing escaping symlink cannot modify a file outside + // the boundary. + if ok, err := files.WithinScope(afs, dst); err != nil || !ok { + return nil, os.ErrPermission + } + dir, _ := path.Split(dst) err := afs.MkdirAll(dir, dirMode) if err != nil { From 5328e80d2e88d1c279a1250a7dfee4fc96f703ec Mon Sep 17 00:00:00 2001 From: Ariel Leyva Date: Wed, 3 Jun 2026 05:22:05 -0400 Subject: [PATCH 16/53] fix: parse csv files with uneven columns in their rows (#5965) --- frontend/src/components/files/CsvViewer.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/files/CsvViewer.vue b/frontend/src/components/files/CsvViewer.vue index 9e1904afd8..5f50bbf307 100644 --- a/frontend/src/components/files/CsvViewer.vue +++ b/frontend/src/components/files/CsvViewer.vue @@ -138,7 +138,11 @@ watchEffect(() => { : props.content; parse( content as string, - { delimiter: columnSeparator.value, skip_empty_lines: true }, + { + delimiter: columnSeparator.value, + skip_empty_lines: true, + relax_column_count: true, + }, (error, output) => { if (error) { console.error("Failed to parse CSV:", error); From 103683069e077fa5976da7bb4b390110a68bdc30 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 11:25:04 +0200 Subject: [PATCH 17/53] chore: Updates for project File Browser (#5947) --- frontend/src/i18n/fr.json | 8 ++++---- frontend/src/i18n/nl.json | 2 +- frontend/src/i18n/pl.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/i18n/fr.json b/frontend/src/i18n/fr.json index e35ed89d5f..985763f1dd 100644 --- a/frontend/src/i18n/fr.json +++ b/frontend/src/i18n/fr.json @@ -29,8 +29,8 @@ "rename": "Renommer", "replace": "Remplacer", "reportIssue": "Signaler un problème", - "resumeTransfer": "Resume previous transfer", - "resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.", + "resumeTransfer": "Reprendre le transfert précédent", + "resumeTransferTooltip": "Ignorez tous les fichiers en conflit, à l'exception de ceux dont la taille est inférieure sur le serveur, car nous supposons que leur transfert a été interrompu.", "save": "Enregistrer", "schedule": "Planifier", "search": "Rechercher", @@ -247,7 +247,7 @@ "execute": "Exécuter des commandes", "modify": "Modifier des fichiers", "rename": "Renommer ou déplacer des fichiers ou des dossiers", - "share": "Share files (require download permission)" + "share": "Partager des fichiers (autorisation de téléchargement requise)" }, "permissions": "Permissions", "permissionsHelp": "Vous pouvez définir l'utilisateur comme étant un administrateur ou encore choisir les permissions individuellement. Si vous sélectionnez \"Administrateur\", toutes les autres options seront automatiquement activées. La gestion des utilisateurs est un privilège que seul l'administrateur possède.\n", @@ -283,7 +283,7 @@ "currentPassword": "Mot de Passe Actuel" }, "sidebar": { - "diskUsed": "{used} of {total} used", + "diskUsed": "{used} sur {total} utilisés", "help": "Aide", "hugoNew": "Nouveau Hugo", "login": "Login", diff --git a/frontend/src/i18n/nl.json b/frontend/src/i18n/nl.json index 69b0919bbf..f8877593d5 100644 --- a/frontend/src/i18n/nl.json +++ b/frontend/src/i18n/nl.json @@ -29,7 +29,7 @@ "rename": "Hernoemen", "replace": "Vervangen", "reportIssue": "Probleem melden", - "resumeTransfer": "Resume previous transfer", + "resumeTransfer": "Vorige overdracht hervatten", "resumeTransferTooltip": "Alle conflicterende bestanden overslaan, behalve de bestanden die kleiner zijn op de server, omdat we veronderstellen dat hun overdracht is onderbroken.", "save": "Opslaan", "schedule": "Plannen", diff --git a/frontend/src/i18n/pl.json b/frontend/src/i18n/pl.json index a3cc6c3270..84668f63cd 100644 --- a/frontend/src/i18n/pl.json +++ b/frontend/src/i18n/pl.json @@ -29,7 +29,7 @@ "rename": "Zmień nazwę", "replace": "Zamień", "reportIssue": "Zgłoś problem", - "resumeTransfer": "Resume previous transfer", + "resumeTransfer": "Wznów poprzedni transfer", "resumeTransferTooltip": "Pomiń wszystkie pliki powodujące konflikty, z wyjątkiem tych, które są mniejsze na serwerze, ponieważ podejrzewamy, że ich transfer został przerwany.", "save": "Zapisz", "schedule": "Harmonogram", From 4edabb9ccc74c8b0bc80f0ac6af121d106ca6647 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 11:30:59 +0200 Subject: [PATCH 18/53] chore(docs): update CLI documentation --- www/docs/cli/filebrowser-config-init.md | 2 +- www/docs/cli/filebrowser-config-set.md | 2 +- www/docs/cli/filebrowser-users-add.md | 2 +- www/docs/cli/filebrowser-users-update.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/www/docs/cli/filebrowser-config-init.md b/www/docs/cli/filebrowser-config-init.md index 0f16efa76b..bf13379e15 100644 --- a/www/docs/cli/filebrowser-config-init.md +++ b/www/docs/cli/filebrowser-config-init.md @@ -42,7 +42,7 @@ filebrowser config init [flags] --disableTypeDetectionByHeader disables type detection by reading file headers --fileMode string mode bits that new files are created with (default "0o640") -h, --help help for init - --hideDotfiles hide dotfiles + --hideDotfiles hide dotfiles in file listings --hideLoginButton hide login button from public pages -k, --key string tls key --locale string locale for users (default "en") diff --git a/www/docs/cli/filebrowser-config-set.md b/www/docs/cli/filebrowser-config-set.md index 603bb506bc..e17f7058b5 100644 --- a/www/docs/cli/filebrowser-config-set.md +++ b/www/docs/cli/filebrowser-config-set.md @@ -39,7 +39,7 @@ filebrowser config set [flags] --disableTypeDetectionByHeader disables type detection by reading file headers --fileMode string mode bits that new files are created with (default "0o640") -h, --help help for set - --hideDotfiles hide dotfiles + --hideDotfiles hide dotfiles in file listings --hideLoginButton hide login button from public pages -k, --key string tls key --locale string locale for users (default "en") diff --git a/www/docs/cli/filebrowser-users-add.md b/www/docs/cli/filebrowser-users-add.md index 1de95e4d26..b433315b75 100644 --- a/www/docs/cli/filebrowser-users-add.md +++ b/www/docs/cli/filebrowser-users-add.md @@ -17,7 +17,7 @@ filebrowser users add [flags] --commands strings a list of the commands a user can execute --dateFormat use date format (true for absolute time, false for relative) -h, --help help for add - --hideDotfiles hide dotfiles + --hideDotfiles hide dotfiles in file listings --locale string locale for users (default "en") --lockPassword lock password --perm.admin admin perm for users diff --git a/www/docs/cli/filebrowser-users-update.md b/www/docs/cli/filebrowser-users-update.md index 4ba7d60860..89a37a1e71 100644 --- a/www/docs/cli/filebrowser-users-update.md +++ b/www/docs/cli/filebrowser-users-update.md @@ -18,7 +18,7 @@ filebrowser users update [flags] --commands strings a list of the commands a user can execute --dateFormat use date format (true for absolute time, false for relative) -h, --help help for update - --hideDotfiles hide dotfiles + --hideDotfiles hide dotfiles in file listings --locale string locale for users (default "en") --lockPassword lock password -p, --password string new password From 85b7d2762dda67b6158220991654c65b43739005 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 11:31:10 +0200 Subject: [PATCH 19/53] chore(release): 2.63.6 --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87ff5e54a6..4f576e0e80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.63.6](https://github.com/filebrowser/filebrowser/compare/v2.63.5...v2.63.6) (2026-06-03) + + +### Bug Fixes + +* address three security disclosures (archive traversal, login DoS, symlink escape) ([847d08b](https://github.com/filebrowser/filebrowser/commit/847d08bdd135e5c3659f2e6dea2f0cd36617af9b)) +* cross-user unauthorized share-link deletion ([0231b7e](https://github.com/filebrowser/filebrowser/commit/0231b7ebdfbe77a6c54027d30c4856c3fd81ee4d)) +* incorrect access control in public directory shares via rule path rebasing ([e07c59d](https://github.com/filebrowser/filebrowser/commit/e07c59df0b850f5924d5b1683e8609661ddcf534)) +* parse csv files with uneven columns in their rows ([#5965](https://github.com/filebrowser/filebrowser/issues/5965)) ([5328e80](https://github.com/filebrowser/filebrowser/commit/5328e80d2e88d1c279a1250a7dfee4fc96f703ec)) +* remove undocumented hook auth with shell replacement ([34ae34e](https://github.com/filebrowser/filebrowser/commit/34ae34e764d72540c039f1f5ea2ec4c974168c1f)) + ## [2.63.5](https://github.com/filebrowser/filebrowser/compare/v2.63.4...v2.63.5) (2026-05-21) From 166583db632e088e9f0adce30aec43bb9d9019f4 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 11:43:22 +0200 Subject: [PATCH 20/53] fix: disallow shares for non-existent paths --- http/share.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/http/share.go b/http/share.go index ec95d06629..a0a15dc9d9 100644 --- a/http/share.go +++ b/http/share.go @@ -99,6 +99,13 @@ var shareDeleteHandler = withPermShare(func(_ http.ResponseWriter, r *http.Reque }) var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { + // Only allow sharing paths that currently exist. Otherwise a share could be + // created for a non-existent path and would silently start exposing + // whatever file later appears there. + if _, err := d.user.Fs.Stat(r.URL.Path); err != nil { + return errToStatus(err), err + } + var s *share.Link var body share.CreateBody if r.Body != nil { From 4488f5b1315c9decd3c04af2b76518745c3e4ab2 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 11:44:54 +0200 Subject: [PATCH 21/53] chore(release): 2.63.7 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f576e0e80..082f4e0147 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.63.7](https://github.com/filebrowser/filebrowser/compare/v2.63.6...v2.63.7) (2026-06-03) + + +### Bug Fixes + +* disallow shares for non-existent paths ([166583d](https://github.com/filebrowser/filebrowser/commit/166583db632e088e9f0adce30aec43bb9d9019f4)) + ## [2.63.6](https://github.com/filebrowser/filebrowser/compare/v2.63.5...v2.63.6) (2026-06-03) From ca019ae7d966a7c28de2b2341271cd13e3458ae6 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 11:57:10 +0200 Subject: [PATCH 22/53] fix: check if share is within scope when creating --- http/share.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/http/share.go b/http/share.go index a0a15dc9d9..c73f5daf58 100644 --- a/http/share.go +++ b/http/share.go @@ -15,6 +15,7 @@ import ( "golang.org/x/crypto/bcrypt" fberrors "github.com/filebrowser/filebrowser/v2/errors" + "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/share" ) @@ -106,6 +107,16 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request return errToStatus(err), err } + // Refuse to create a share whose on-disk target escapes the user's scope + // (e.g. via a symlink), mirroring the read/write guards. The public serve + // path already rejects these, but blocking creation avoids dangling shares. + if ok, err := files.WithinScope(d.user.Fs, r.URL.Path); err != nil || !ok { + if err != nil { + return errToStatus(err), err + } + return http.StatusForbidden, nil + } + var s *share.Link var body share.CreateBody if r.Body != nil { From f5e0c4e2e18902e51d6264ff87874265fc623c62 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 11:57:30 +0200 Subject: [PATCH 23/53] chore(release): 2.63.8 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 082f4e0147..8ab9463ffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.63.8](https://github.com/filebrowser/filebrowser/compare/v2.63.7...v2.63.8) (2026-06-03) + + +### Bug Fixes + +* check if share is within scope when creating ([ca019ae](https://github.com/filebrowser/filebrowser/commit/ca019ae7d966a7c28de2b2341271cd13e3458ae6)) + ## [2.63.7](https://github.com/filebrowser/filebrowser/compare/v2.63.6...v2.63.7) (2026-06-03) From 103acd15fe57554fe0246bfe70a49b6cb4ae0c51 Mon Sep 17 00:00:00 2001 From: mehmet turac Date: Wed, 3 Jun 2026 12:59:17 +0300 Subject: [PATCH 24/53] fix: force octet-stream for attachment downloads (#5942) --- http/raw.go | 1 + http/raw_test.go | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/http/raw.go b/http/raw.go index 35e474ae21..b057e95ed1 100644 --- a/http/raw.go +++ b/http/raw.go @@ -77,6 +77,7 @@ func setContentDisposition(w http.ResponseWriter, r *http.Request, file *files.F } else { // As per RFC6266 section 4.3 w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(file.Name)) + w.Header().Set("Content-Type", "application/octet-stream") } } diff --git a/http/raw_test.go b/http/raw_test.go index ec53a17334..c35334f6b6 100644 --- a/http/raw_test.go +++ b/http/raw_test.go @@ -70,6 +70,14 @@ func TestSetContentDisposition(t *testing.T) { if got != tc.expected { t.Errorf("Content-Disposition = %q, want %q", got, tc.expected) } + + contentType := recorder.Header().Get("Content-Type") + if tc.inline && contentType != "" { + t.Errorf("Content-Type = %q, want empty", contentType) + } + if !tc.inline && contentType != "application/octet-stream" { + t.Errorf("Content-Type = %q, want application/octet-stream", contentType) + } }) } } From cdd666fc95f569ad583c32391e45646fed676dfd Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 12:21:23 +0200 Subject: [PATCH 25/53] fix: prevent symlink scope escape in copy/move/rename Check WithinScope for src and dst in resourcePatchHandler before fileutils.Copy/MoveFile, which follow symlinks and bypassed the stat()/writeFile() guards (GHSA-239w-m3h6-ch8v). --- http/resource.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/http/resource.go b/http/resource.go index 9f7bfb0176..5f06a7cdfb 100644 --- a/http/resource.go +++ b/http/resource.go @@ -228,6 +228,19 @@ func resourcePatchHandler(fileCache FileCache) handleFunc { return http.StatusForbidden, nil } + // Refuse to copy/move through a symlink that escapes the user's scope, + // for either the source (read escape) or the destination (write + // escape). fileutils.Copy/MoveFile operate on the raw afero FS and + // follow symlinks, so they bypass the guards in stat()/writeFile(). + for _, p := range []string{src, dst} { + if ok, scopeErr := files.WithinScope(d.user.Fs, p); scopeErr != nil || !ok { + if scopeErr != nil { + return errToStatus(scopeErr), scopeErr + } + return http.StatusForbidden, nil + } + } + err = checkParent(src, dst) if err != nil { return http.StatusBadRequest, err From 19514367adf2d9fe5be2b7666e397979ea679b94 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 12:21:23 +0200 Subject: [PATCH 26/53] fix: use constant-time comparison for share access token Compare the share token with subtle.ConstantTimeCompare instead of ==. --- http/public.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/http/public.go b/http/public.go index bf49927c71..fb71d2b8bf 100644 --- a/http/public.go +++ b/http/public.go @@ -1,6 +1,7 @@ package fbhttp import ( + "crypto/subtle" "errors" "net/http" "net/url" @@ -136,7 +137,7 @@ func authenticateShareRequest(r *http.Request, l *share.Link) (int, error) { return 0, nil } - if r.URL.Query().Get("token") == l.Token { + if subtle.ConstantTimeCompare([]byte(r.URL.Query().Get("token")), []byte(l.Token)) == 1 { return 0, nil } From 35db07d0159c520a6b3c969ac52033593914fadd Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 12:21:23 +0200 Subject: [PATCH 27/53] fix: set X-Content-Type-Options: nosniff on raw file responses Prevents browsers from MIME-sniffing uploaded files into a renderable type. --- http/raw.go | 1 + 1 file changed, 1 insertion(+) diff --git a/http/raw.go b/http/raw.go index b057e95ed1..f0b124551c 100644 --- a/http/raw.go +++ b/http/raw.go @@ -225,6 +225,7 @@ func rawFileHandler(w http.ResponseWriter, r *http.Request, file *files.FileInfo setContentDisposition(w, r, file) w.Header().Add("Content-Security-Policy", `script-src 'none';`) + w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("Cache-Control", "private") http.ServeContent(w, r, file.Name, file.ModTime, fd) return 0, nil From 503fd6b01f311bf432b3fcf840acc5c79f23148f Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 13:13:03 +0200 Subject: [PATCH 28/53] chore(release): 2.63.9 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ab9463ffe..74f7434022 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.63.9](https://github.com/filebrowser/filebrowser/compare/v2.63.8...v2.63.9) (2026-06-03) + + +### Bug Fixes + +* force octet-stream for attachment downloads ([#5942](https://github.com/filebrowser/filebrowser/issues/5942)) ([103acd1](https://github.com/filebrowser/filebrowser/commit/103acd15fe57554fe0246bfe70a49b6cb4ae0c51)) +* prevent symlink scope escape in copy/move/rename ([cdd666f](https://github.com/filebrowser/filebrowser/commit/cdd666fc95f569ad583c32391e45646fed676dfd)) +* set X-Content-Type-Options: nosniff on raw file responses ([35db07d](https://github.com/filebrowser/filebrowser/commit/35db07d0159c520a6b3c969ac52033593914fadd)) +* use constant-time comparison for share access token ([1951436](https://github.com/filebrowser/filebrowser/commit/19514367adf2d9fe5be2b7666e397979ea679b94)) + ## [2.63.8](https://github.com/filebrowser/filebrowser/compare/v2.63.7...v2.63.8) (2026-06-03) From 6b04cbf5e9db1f5b9c0b1624843607ce2881cfc4 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 15:40:26 +0200 Subject: [PATCH 29/53] fix: allow writes when user scope resolves to filesystem root WithinScope compared targets against root+separator, which produced "//" when the scope resolved to "/". No path matched, so every write was rejected with os.ErrPermission (HTTP 403), breaking saves and uploads for root-scoped installs. Skip the appended separator when root already ends in one. --- files/file.go | 12 ++++++- files/file_test.go | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 files/file_test.go diff --git a/files/file.go b/files/file.go index 3b38f31459..dc227a6caf 100644 --- a/files/file.go +++ b/files/file.go @@ -217,7 +217,17 @@ func WithinScope(fsys afero.Fs, p string) (bool, error) { return false, err } - return resolved == root || strings.HasPrefix(resolved, root+string(filepath.Separator)), nil + // Compare against root with a trailing separator so a sibling like + // "/srvother" is not treated as being inside "/srv". When root is itself + // the filesystem boundary (e.g. "/"), it already ends in a separator, so + // avoid producing "//" — which no path would match — and accept any path + // under it. + prefix := root + if !strings.HasSuffix(prefix, string(filepath.Separator)) { + prefix += string(filepath.Separator) + } + + return resolved == root || strings.HasPrefix(resolved, prefix), nil } // Checksum checksums a given File for a given User, using a specific diff --git a/files/file_test.go b/files/file_test.go new file mode 100644 index 0000000000..0f89f5724e --- /dev/null +++ b/files/file_test.go @@ -0,0 +1,83 @@ +package files + +import ( + "os" + "path/filepath" + "testing" + + "github.com/spf13/afero" +) + +func TestWithinScope(t *testing.T) { + t.Run("non-scoped filesystem is a no-op", func(t *testing.T) { + ok, err := WithinScope(afero.NewOsFs(), "/anything") + if err != nil || !ok { + t.Fatalf("expected (true, nil), got (%v, %v)", ok, err) + } + }) + + t.Run("path inside a nested scope is allowed", func(t *testing.T) { + scope := t.TempDir() + if err := os.WriteFile(filepath.Join(scope, "file.txt"), []byte("x"), 0o644); err != nil { + t.Fatal(err) + } + bfs := afero.NewBasePathFs(afero.NewOsFs(), scope) + + ok, err := WithinScope(bfs, "/file.txt") + if err != nil || !ok { + t.Fatalf("expected (true, nil), got (%v, %v)", ok, err) + } + }) + + t.Run("new file inside scope is allowed", func(t *testing.T) { + scope := t.TempDir() + bfs := afero.NewBasePathFs(afero.NewOsFs(), scope) + + ok, err := WithinScope(bfs, "/does-not-exist-yet.txt") + if err != nil || !ok { + t.Fatalf("expected (true, nil), got (%v, %v)", ok, err) + } + }) + + // Regression for #5975: when the scope resolves to the filesystem root, + // root+separator used to be "//", which no path matched, so every write + // was rejected with os.ErrPermission (HTTP 403). + t.Run("filesystem root scope allows writes", func(t *testing.T) { + f := filepath.Join(t.TempDir(), "file.txt") + if err := os.WriteFile(f, []byte("x"), 0o644); err != nil { + t.Fatal(err) + } + bfs := afero.NewBasePathFs(afero.NewOsFs(), "/") + + ok, err := WithinScope(bfs, f) + if err != nil || !ok { + t.Fatalf("expected (true, nil) for a path under root scope, got (%v, %v)", ok, err) + } + }) + + t.Run("sibling of a nested scope is rejected", func(t *testing.T) { + base := t.TempDir() + scope := filepath.Join(base, "srv") + sibling := filepath.Join(base, "srvother") + for _, d := range []string{scope, sibling} { + if err := os.MkdirAll(d, 0o755); err != nil { + t.Fatal(err) + } + } + // A symlink lexically inside the scope pointing at a sibling directory + // must not be followed. + link := filepath.Join(scope, "escape") + if err := os.Symlink(sibling, link); err != nil { + t.Fatal(err) + } + bfs := afero.NewBasePathFs(afero.NewOsFs(), scope) + + ok, err := WithinScope(bfs, "/escape") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if ok { + t.Fatal("expected escaping symlink to a sibling directory to be rejected") + } + }) +} From 69c76d11cc865e1d959f715f7b35bb905c14079f Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 3 Jun 2026 15:42:27 +0200 Subject: [PATCH 30/53] chore(release): 2.63.10 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74f7434022..d9f6b65d91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.63.10](https://github.com/filebrowser/filebrowser/compare/v2.63.9...v2.63.10) (2026-06-03) + + +### Bug Fixes + +* allow writes when user scope resolves to filesystem root ([6b04cbf](https://github.com/filebrowser/filebrowser/commit/6b04cbf5e9db1f5b9c0b1624843607ce2881cfc4)) + ## [2.63.9](https://github.com/filebrowser/filebrowser/compare/v2.63.8...v2.63.9) (2026-06-03) From 3471ec2c4b6473831c72ee889cb3c1a6849a1fb1 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 4 Jun 2026 11:02:18 +0200 Subject: [PATCH 31/53] fix: incomplete fix for symlinked directories let scopes users and public-share recipients read and write files outside of scope --- files/file.go | 23 ++--- files/file_test.go | 50 +++++++++++ http/public_symlink_test.go | 166 ++++++++++++++++++++++++++++++++++++ http/public_test.go | 8 +- http/raw.go | 4 + http/tus_handlers.go | 10 +++ http/tus_symlink_test.go | 118 +++++++++++++++++++++++++ 7 files changed, 368 insertions(+), 11 deletions(-) create mode 100644 http/public_symlink_test.go create mode 100644 http/tus_symlink_test.go diff --git a/files/file.go b/files/file.go index dc227a6caf..a034abc0ca 100644 --- a/files/file.go +++ b/files/file.go @@ -128,22 +128,18 @@ func stat(opts *FileOptions) (*FileInfo, error) { } } - // regular file - if file != nil && !file.IsSymlink { - return file, nil - } - - // The path is a symlink. Refuse to follow it if its on-disk target escapes - // the user's scoped root; otherwise a symlink that lives lexically inside - // the scope but points outside it would let a restricted user read, write, - // or share files beyond their boundary. - if file != nil && file.IsSymlink { + if file != nil { ok, scopeErr := WithinScope(opts.Fs, opts.Path) if scopeErr != nil || !ok { return nil, os.ErrPermission } } + // regular file + if file != nil && !file.IsSymlink { + return file, nil + } + // fs doesn't support afero.Lstater interface or the file is a symlink info, err := opts.Fs.Stat(opts.Path) if err != nil { @@ -479,6 +475,13 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool, calcImgRe isSymlink, isInvalidLink := false, false if IsSymlink(f.Mode()) { isSymlink = true + // A symlink whose on-disk target escapes the scoped root must not be + // followed, otherwise the listing would leak the target's metadata + // (and downstream access) for files outside the user's scope or the + // shared subtree. + if ok, scopeErr := WithinScope(i.Fs, fPath); scopeErr != nil || !ok { + continue + } // It's a symbolic link. We try to follow it. If it doesn't work, // we stay with the link information instead of the target's. info, err := i.Fs.Stat(fPath) diff --git a/files/file_test.go b/files/file_test.go index 0f89f5724e..d1f647f348 100644 --- a/files/file_test.go +++ b/files/file_test.go @@ -80,4 +80,54 @@ func TestWithinScope(t *testing.T) { t.Fatal("expected escaping symlink to a sibling directory to be rejected") } }) + + t.Run("symlink whose target stays within scope is allowed", func(t *testing.T) { + scope := t.TempDir() + if err := os.MkdirAll(filepath.Join(scope, "real"), 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(scope, "real", "f.txt"), []byte("x"), 0o644); err != nil { + t.Fatal(err) + } + if err := os.Symlink(filepath.Join(scope, "real"), filepath.Join(scope, "link")); err != nil { + t.Skipf("cannot create symlink: %v", err) + } + bfs := afero.NewBasePathFs(afero.NewOsFs(), scope) + + ok, err := WithinScope(bfs, "/link/f.txt") + if err != nil || !ok { + t.Fatalf("expected (true, nil) for an in-scope symlink target, got (%v, %v)", ok, err) + } + }) +} + +// stat must reject a regular file reached through a symlinked ancestor that +// escapes the scope (GHSA-hf77-9m7w-fq8q), while still serving in-scope files. +func TestStatRejectsLinkedAncestorEscape(t *testing.T) { + scope := t.TempDir() + if err := os.MkdirAll(filepath.Join(scope, "shared"), 0o755); err != nil { + t.Fatal(err) + } + if err := os.MkdirAll(filepath.Join(scope, "private"), 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(scope, "private", "secret.txt"), []byte("secret"), 0o600); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(scope, "shared", "ok.txt"), []byte("ok"), 0o600); err != nil { + t.Fatal(err) + } + if err := os.Symlink(filepath.Join(scope, "private"), filepath.Join(scope, "shared", "link")); err != nil { + t.Skipf("cannot create symlink: %v", err) + } + + // Filesystem scoped to the shared directory, as a public share would be. + bfs := afero.NewBasePathFs(afero.NewOsFs(), filepath.Join(scope, "shared")) + + if _, err := stat(&FileOptions{Fs: bfs, Path: "/link/secret.txt"}); !os.IsPermission(err) { + t.Fatalf("expected permission error for linked-ancestor escape, got %v", err) + } + if _, err := stat(&FileOptions{Fs: bfs, Path: "/ok.txt"}); err != nil { + t.Fatalf("expected in-scope file to be served, got %v", err) + } } diff --git a/http/public_symlink_test.go b/http/public_symlink_test.go new file mode 100644 index 0000000000..89c3bbd35a --- /dev/null +++ b/http/public_symlink_test.go @@ -0,0 +1,166 @@ +package fbhttp + +import ( + "archive/zip" + "bytes" + "io" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/asdine/storm/v3" + "github.com/spf13/afero" + + "github.com/filebrowser/filebrowser/v2/settings" + "github.com/filebrowser/filebrowser/v2/share" + "github.com/filebrowser/filebrowser/v2/storage" + "github.com/filebrowser/filebrowser/v2/storage/bolt" + "github.com/filebrowser/filebrowser/v2/users" +) + +// symlinkShareStorage builds a storage whose single user is rooted at a real +// on-disk scope containing a public share "/shared" with a symlinked +// descendant "link -> ../private". Skips the test if symlinks are unavailable. +func symlinkShareStorage(t *testing.T) *storage.Storage { + t.Helper() + scope := t.TempDir() + if err := os.MkdirAll(filepath.Join(scope, "shared"), 0o755); err != nil { + t.Fatal(err) + } + if err := os.MkdirAll(filepath.Join(scope, "private"), 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(scope, "private", "secret.txt"), []byte("symlink-secret"), 0o600); err != nil { + t.Fatal(err) + } + if err := os.Symlink(filepath.Join(scope, "private"), filepath.Join(scope, "shared", "link")); err != nil { + t.Skipf("cannot create symlink on this platform: %v", err) + } + + db, err := storm.Open(filepath.Join(t.TempDir(), "db")) + if err != nil { + t.Fatalf("failed to open db: %v", err) + } + t.Cleanup(func() { _ = db.Close() }) + + st, err := bolt.NewStorage(db) + if err != nil { + t.Fatalf("failed to get storage: %v", err) + } + if err := st.Share.Save(&share.Link{Hash: "h", UserID: 1, Path: "/shared"}); err != nil { + t.Fatalf("failed to save share: %v", err) + } + if err := st.Users.Save(&users.User{ + Username: "username", + Password: "pw", + Perm: users.Permissions{Share: true, Download: true}, + }); err != nil { + t.Fatalf("failed to save user: %v", err) + } + if err := st.Settings.Save(&settings.Settings{Key: []byte("key")}); err != nil { + t.Fatalf("failed to save settings: %v", err) + } + st.Users = &customFSUser{ + Store: st.Users, + fs: afero.NewBasePathFs(afero.NewOsFs(), scope), + } + return st +} + +// Reproduces GHSA-hf77-9m7w-fq8q: a public directory share whose subtree +// contains a symlink to a directory outside the share. Requesting a regular +// file behind that linked ancestor must NOT disclose its contents. +func TestPublicShareSymlinkDescendantDisclosure(t *testing.T) { + cases := map[string]struct { + handler handleFunc + path string + }{ + "direct file download via dl handler": {handler: publicDlHandler, path: "h/link/secret.txt"}, + "share info via share handler": {handler: publicShareHandler, path: "h/link/secret.txt"}, + "listing of linked dir": {handler: publicShareHandler, path: "h/link/"}, + } + + for name, tc := range cases { + tc := tc + t.Run(name, func(t *testing.T) { + st := symlinkShareStorage(t) + + req := newHTTPRequest(t, func(r *http.Request) { r.URL.Path = tc.path }) + recorder := httptest.NewRecorder() + handler := handle(tc.handler, "", st, &settings.Server{}) + handler.ServeHTTP(recorder, req) + + result := recorder.Result() + defer result.Body.Close() + body, _ := io.ReadAll(result.Body) + + t.Logf("status=%d body=%q", result.StatusCode, string(body)) + if result.StatusCode == http.StatusOK { + t.Errorf("VULNERABLE: leaked path outside share (status 200, body=%q)", string(body)) + } + }) + } +} + +// The listing of the public share root must omit the escaping symlink "link" +// entirely (no target metadata leak). +func TestPublicShareSymlinkListingOmitsEscapingLink(t *testing.T) { + st := symlinkShareStorage(t) + + req := newHTTPRequest(t, func(r *http.Request) { r.URL.Path = "h/" }) + recorder := httptest.NewRecorder() + handler := handle(publicShareHandler, "", st, &settings.Server{}) + handler.ServeHTTP(recorder, req) + + result := recorder.Result() + defer result.Body.Close() + body, _ := io.ReadAll(result.Body) + if result.StatusCode != http.StatusOK { + t.Fatalf("share root listing failed: status=%d body=%q", result.StatusCode, string(body)) + } + if strings.Contains(string(body), "\"link\"") { + t.Errorf("VULNERABLE: listing exposes escaping symlink: %s", string(body)) + } +} + +// Reproduces the archive variant of GHSA-hf77-9m7w-fq8q: downloading the whole +// public share as a zip must not pull in files reached through a symlinked +// descendant. +func TestPublicShareSymlinkArchiveDisclosure(t *testing.T) { + st := symlinkShareStorage(t) + + // Request the whole share root as an archive. + req := newHTTPRequest(t, func(r *http.Request) { r.URL.Path = "h/" }) + recorder := httptest.NewRecorder() + handler := handle(publicDlHandler, "", st, &settings.Server{}) + handler.ServeHTTP(recorder, req) + + result := recorder.Result() + defer result.Body.Close() + body, _ := io.ReadAll(result.Body) + if result.StatusCode != http.StatusOK { + t.Fatalf("archive request failed: status=%d body=%q", result.StatusCode, string(body)) + } + + zr, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) + if err != nil { + t.Fatalf("failed to read zip: %v", err) + } + for _, f := range zr.File { + if strings.Contains(f.Name, "secret.txt") { + t.Errorf("VULNERABLE: archive includes file behind symlinked descendant: %q", f.Name) + } + rc, err := f.Open() + if err != nil { + continue + } + content, _ := io.ReadAll(rc) + rc.Close() + if bytes.Contains(content, []byte("symlink-secret")) { + t.Errorf("VULNERABLE: archive entry %q leaks out-of-scope content", f.Name) + } + } +} diff --git a/http/public_test.go b/http/public_test.go index bc57a9cac7..947ee04947 100644 --- a/http/public_test.go +++ b/http/public_test.go @@ -220,7 +220,13 @@ func TestPublicShareHandlerRules(t *testing.T) { t.Fatalf("failed to save settings: %v", err) } - fs := afero.NewMemMapFs() + fs := afero.NewBasePathFs(afero.NewOsFs(), t.TempDir()) + if err := fs.MkdirAll("/projects/private", 0o755); err != nil { + t.Fatalf("failed to create private dir: %v", err) + } + if err := fs.MkdirAll("/projects/public", 0o755); err != nil { + t.Fatalf("failed to create public dir: %v", err) + } if err := afero.WriteFile(fs, "/projects/private/secret.txt", []byte("top secret"), 0o600); err != nil { t.Fatalf("failed to write secret file: %v", err) } diff --git a/http/raw.go b/http/raw.go index f0b124551c..ab01eed6ad 100644 --- a/http/raw.go +++ b/http/raw.go @@ -115,6 +115,10 @@ func getFiles(d *data, path, commonPath string) ([]archives.FileInfo, error) { return nil, nil } + if ok, err := files.WithinScope(d.user.Fs, path); err != nil || !ok { + return nil, nil + } + info, err := d.user.Fs.Stat(path) if err != nil { return nil, err diff --git a/http/tus_handlers.go b/http/tus_handlers.go index 31245a3418..8a68161d12 100644 --- a/http/tus_handlers.go +++ b/http/tus_handlers.go @@ -44,6 +44,11 @@ func tusPostHandler(cache UploadCache) handleFunc { if !d.user.Perm.Create || !d.Check(r.URL.Path) { return http.StatusForbidden, nil } + + if ok, scopeErr := files.WithinScope(d.user.Fs, r.URL.Path); scopeErr != nil || !ok { + return http.StatusForbidden, nil + } + file, err := files.NewFileInfo(&files.FileOptions{ Fs: d.user.Fs, Path: r.URL.Path, @@ -161,6 +166,11 @@ func tusPatchHandler(cache UploadCache) handleFunc { if r.Header.Get("Content-Type") != "application/offset+octet-stream" { return http.StatusUnsupportedMediaType, nil } + // Defense in depth: refuse to write through a symlink that escapes the + // scope, in case the target path is (or sits behind) an escaping link. + if ok, scopeErr := files.WithinScope(d.user.Fs, r.URL.Path); scopeErr != nil || !ok { + return http.StatusForbidden, nil + } uploadOffset, err := getUploadOffset(r) if err != nil { diff --git a/http/tus_symlink_test.go b/http/tus_symlink_test.go new file mode 100644 index 0000000000..ac0b8c3db0 --- /dev/null +++ b/http/tus_symlink_test.go @@ -0,0 +1,118 @@ +package fbhttp + +import ( + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + "time" + + "github.com/asdine/storm/v3" + "github.com/golang-jwt/jwt/v5" + "github.com/spf13/afero" + + "github.com/filebrowser/filebrowser/v2/settings" + "github.com/filebrowser/filebrowser/v2/storage/bolt" + "github.com/filebrowser/filebrowser/v2/users" +) + +// Reproduces the TUS write vector of GHSA-v9g6-9pp4-3w22: a scoped user must +// not be able to create or write files through a symlinked directory that +// escapes their scope. +func TestTusHandlersRejectSymlinkScopeEscape(t *testing.T) { + root := t.TempDir() + userScope := filepath.Join(root, "user") + outside := filepath.Join(root, "otheruser") + for _, d := range []string{userScope, outside} { + if err := os.MkdirAll(d, 0o755); err != nil { + t.Fatal(err) + } + } + // A directory symlink inside the user's scope pointing outside it. + if err := os.Symlink(outside, filepath.Join(userScope, "escape_link")); err != nil { + t.Skipf("cannot create symlink: %v", err) + } + + key := []byte("test-signing-key") + + db, err := storm.Open(filepath.Join(t.TempDir(), "db")) + if err != nil { + t.Fatalf("failed to open db: %v", err) + } + t.Cleanup(func() { _ = db.Close() }) + + st, err := bolt.NewStorage(db) + if err != nil { + t.Fatalf("failed to get storage: %v", err) + } + if err := st.Users.Save(&users.User{ + Username: "u", + Password: "pw", + Perm: users.Permissions{Create: true, Modify: true}, + }); err != nil { + t.Fatalf("failed to save user: %v", err) + } + if err := st.Settings.Save(&settings.Settings{Key: key}); err != nil { + t.Fatalf("failed to save settings: %v", err) + } + st.Users = &customFSUser{ + Store: st.Users, + fs: afero.NewBasePathFs(afero.NewOsFs(), userScope), + } + + // Forge a valid auth token for user ID 1. + claims := &authToken{ + User: userInfo{ID: 1, Username: "u", Perm: users.Permissions{Create: true, Modify: true}}, + RegisteredClaims: jwt.RegisteredClaims{ + IssuedAt: jwt.NewNumericDate(time.Now().Add(-time.Minute)), + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), + }, + } + signed, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(key) + if err != nil { + t.Fatalf("failed to sign token: %v", err) + } + + cases := map[string]struct { + method string + handler handleFunc + headers map[string]string + }{ + "POST create through symlinked dir": { + method: http.MethodPost, + handler: tusPostHandler(newMemoryUploadCache()), + headers: map[string]string{"Upload-Length": "20"}, + }, + "PATCH write through symlinked dir": { + method: http.MethodPatch, + handler: tusPatchHandler(newMemoryUploadCache()), + headers: map[string]string{"Content-Type": "application/offset+octet-stream", "Upload-Offset": "0"}, + }, + } + + for name, tc := range cases { + tc := tc + t.Run(name, func(t *testing.T) { + req, err := http.NewRequest(tc.method, "escape_link/injected.txt", http.NoBody) + if err != nil { + t.Fatal(err) + } + req.Header.Set("X-Auth", signed) + for k, v := range tc.headers { + req.Header.Set(k, v) + } + + recorder := httptest.NewRecorder() + handler := handle(tc.handler, "", st, &settings.Server{}) + handler.ServeHTTP(recorder, req) + + if recorder.Code != http.StatusForbidden { + t.Errorf("expected 403, got %d", recorder.Code) + } + if _, statErr := os.Stat(filepath.Join(outside, "injected.txt")); statErr == nil { + t.Errorf("VULNERABLE: file was created outside the user's scope") + } + }) + } +} From 1086903c14553545aef0f1a5321011cb139aca3d Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 4 Jun 2026 11:03:17 +0200 Subject: [PATCH 32/53] chore(release): 2.63.11 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9f6b65d91..f8c5c235b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.63.11](https://github.com/filebrowser/filebrowser/compare/v2.63.10...v2.63.11) (2026-06-04) + + +### Bug Fixes + +* incomplete fix for symlinked directories let scopes users and public-share recipients read and write files outside of scope ([3471ec2](https://github.com/filebrowser/filebrowser/commit/3471ec2c4b6473831c72ee889cb3c1a6849a1fb1)) + ## [2.63.10](https://github.com/filebrowser/filebrowser/compare/v2.63.9...v2.63.10) (2026-06-03) From 7b7ff8ae8f97393b2e6ae6e061c1f780077c32b6 Mon Sep 17 00:00:00 2001 From: Puneet Dixit Date: Thu, 4 Jun 2026 16:19:07 +0530 Subject: [PATCH 33/53] fix: skip inaccessible children when listing directories (#5958) --- files/file.go | 57 +++++++++++++++++++++++++++-- files/file_test.go | 89 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 2 deletions(-) diff --git a/files/file.go b/files/file.go index a034abc0ca..e84c7e2aa5 100644 --- a/files/file.go +++ b/files/file.go @@ -18,6 +18,7 @@ import ( "path" "path/filepath" "regexp" + "sort" "strings" "time" @@ -452,8 +453,7 @@ func (i *FileInfo) addSubtitle(fPath string) { } func (i *FileInfo) readListing(checker rules.Checker, readHeader bool, calcImgRes bool) error { - afs := &afero.Afero{Fs: i.Fs} - dir, err := afs.ReadDir(i.Path) + dir, err := readDir(i.Fs, i.Path) if err != nil { return err } @@ -535,3 +535,56 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool, calcImgRe i.Listing = listing return nil } + +func readDir(afs afero.Fs, dirname string) ([]os.FileInfo, error) { + dir, err := afero.ReadDir(afs, dirname) + if err == nil { + return dir, nil + } + + dir, fallbackErr := readDirNames(afs, dirname) + if fallbackErr != nil { + return nil, err + } + + return dir, nil +} + +func readDirNames(afs afero.Fs, dirname string) ([]os.FileInfo, error) { + file, err := afs.Open(dirname) + if err != nil { + return nil, err + } + + names, err := file.Readdirnames(-1) + if closeErr := file.Close(); err == nil && closeErr != nil { + err = closeErr + } + if err != nil { + return nil, err + } + + sort.Strings(names) + dir := make([]os.FileInfo, 0, len(names)) + for _, name := range names { + fPath := path.Join(dirname, name) + info, err := lstatIfPossible(afs, fPath) + if err != nil { + log.Printf("Skipping inaccessible file %s: %v", fPath, err) + continue + } + + dir = append(dir, info) + } + + return dir, nil +} + +func lstatIfPossible(afs afero.Fs, name string) (os.FileInfo, error) { + if lstaterFs, ok := afs.(afero.Lstater); ok { + info, _, err := lstaterFs.LstatIfPossible(name) + return info, err + } + + return afs.Stat(name) +} diff --git a/files/file_test.go b/files/file_test.go index d1f647f348..9b46a85dbd 100644 --- a/files/file_test.go +++ b/files/file_test.go @@ -2,6 +2,7 @@ package files import ( "os" + "path" "path/filepath" "testing" @@ -131,3 +132,91 @@ func TestStatRejectsLinkedAncestorEscape(t *testing.T) { t.Fatalf("expected in-scope file to be served, got %v", err) } } + +type allowAllChecker struct{} + +func (allowAllChecker) Check(string) bool { + return true +} + +type inaccessibleChildFs struct { + afero.Fs + child string +} + +func (fs inaccessibleChildFs) Open(name string) (afero.File, error) { + file, err := fs.Fs.Open(name) + if err != nil { + return nil, err + } + + if path.Clean(name) == "/" { + return inaccessibleChildDir{File: file}, nil + } + + return file, nil +} + +func (fs inaccessibleChildFs) Stat(name string) (os.FileInfo, error) { + if path.Clean(name) == fs.child { + return nil, os.ErrPermission + } + + return fs.Fs.Stat(name) +} + +func (fs inaccessibleChildFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { + if path.Clean(name) == fs.child { + return nil, false, os.ErrPermission + } + + if lstater, ok := fs.Fs.(afero.Lstater); ok { + return lstater.LstatIfPossible(name) + } + + info, err := fs.Fs.Stat(name) + return info, false, err +} + +type inaccessibleChildDir struct { + afero.File +} + +func (dir inaccessibleChildDir) Readdir(int) ([]os.FileInfo, error) { + return nil, os.ErrPermission +} + +func TestReadListingSkipsInaccessibleChildren(t *testing.T) { + memFs := afero.NewMemMapFs() + for _, dir := range []string{"/media", "/proton-mount"} { + if err := memFs.Mkdir(dir, 0o755); err != nil { + t.Fatal(err) + } + } + + file, err := NewFileInfo(&FileOptions{ + Fs: inaccessibleChildFs{Fs: memFs, child: "/proton-mount"}, + Path: "/", + Expand: true, + Checker: allowAllChecker{}, + }) + if err != nil { + t.Fatal(err) + } + + if file.Listing == nil { + t.Fatal("expected root listing") + } + + if got := len(file.Items); got != 1 { + t.Fatalf("expected one accessible child, got %d", got) + } + + if got := file.Items[0].Name; got != "media" { + t.Fatalf("expected accessible child to be listed, got %q", got) + } + + if got := file.NumDirs; got != 1 { + t.Fatalf("expected one listed directory, got %d", got) + } +} From 0bb2768754d11b865d68e72dcd7cebb232a6308a Mon Sep 17 00:00:00 2001 From: Puneet Dixit Date: Thu, 4 Jun 2026 16:19:30 +0530 Subject: [PATCH 34/53] fix: keep mobile file sort controls visible (#5977) --- frontend/src/css/__tests__/mobile.test.ts | 22 ++++++++++++++ frontend/src/css/mobile.css | 35 ++++++++++++++++++++--- 2 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 frontend/src/css/__tests__/mobile.test.ts diff --git a/frontend/src/css/__tests__/mobile.test.ts b/frontend/src/css/__tests__/mobile.test.ts new file mode 100644 index 0000000000..8b492c554f --- /dev/null +++ b/frontend/src/css/__tests__/mobile.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "vitest"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +const mobileCss = readFileSync(resolve(__dirname, "../mobile.css"), "utf8"); + +const normalizedCss = mobileCss.replace(/\s+/g, " "); + +describe("mobile file listing styles", () => { + it("hides file row metadata without hiding list header sort controls", () => { + expect(normalizedCss).toContain( + "#listing.list .item:not(.header) .size { display: none;" + ); + expect(normalizedCss).toContain( + "#listing.list .item:not(.header) .modified { display: none;" + ); + + expect(normalizedCss).not.toMatch( + /#listing\.list \.item \.(size|modified) \{ display: none;/ + ); + }); +}); diff --git a/frontend/src/css/mobile.css b/frontend/src/css/mobile.css index f8b2a45b35..baace2036e 100644 --- a/frontend/src/css/mobile.css +++ b/frontend/src/css/mobile.css @@ -14,12 +14,27 @@ body { padding-bottom: 5em; } - #listing.list .item .size { + #listing.list .item:not(.header) .size { display: none; } - #listing.list .item .name { + #listing.list .item:not(.header) .name { width: 60%; } + #listing.list .item.header .name { + margin-right: 0; + width: 45%; + } + #listing.list .item.header .size { + width: 25%; + } + #listing.list .item.header .modified { + width: 30%; + } + #listing.list .item.header p { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } #more { display: inherit; } @@ -162,10 +177,22 @@ } @media (max-width: 450px) { - #listing.list .item .modified { + #listing.list .item:not(.header) .modified { display: none; } - #listing.list .item .name { + #listing.list .item:not(.header) .name { width: 100%; } + #listing.list .item.header { + padding: 0.75em 0.5em; + } + #listing.list .item.header .name { + width: 34%; + } + #listing.list .item.header .size { + width: 24%; + } + #listing.list .item.header .modified { + width: 42%; + } } From c1abe8f561208bf36bde70879d1a15ef9de998fa Mon Sep 17 00:00:00 2001 From: Puneet Dixit Date: Thu, 4 Jun 2026 20:46:30 +0530 Subject: [PATCH 35/53] fix: await copy move conflict detection (#5978) --- frontend/src/components/prompts/Copy.vue | 3 +- frontend/src/components/prompts/Move.vue | 3 +- .../__tests__/copy-move-conflict.test.ts | 125 ++++++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 frontend/src/components/prompts/__tests__/copy-move-conflict.test.ts diff --git a/frontend/src/components/prompts/Copy.vue b/frontend/src/components/prompts/Copy.vue index a24f0fed3c..0073df8dcb 100644 --- a/frontend/src/components/prompts/Copy.vue +++ b/frontend/src/components/prompts/Copy.vue @@ -92,6 +92,7 @@ export default { to: this.dest + encodeURIComponent(this.req.items[item].name), name: this.req.items[item].name, size: this.req.items[item].size, + isDir: this.req.items[item].isDir, modified: this.req.items[item].modified, overwrite: false, rename: this.$route.path === this.dest, @@ -122,7 +123,7 @@ export default { }); }; - const conflict = upload.checkConflict(items, this.dest); + const conflict = await upload.checkConflict(items, this.dest); if (conflict.length > 0) { this.showHover({ diff --git a/frontend/src/components/prompts/Move.vue b/frontend/src/components/prompts/Move.vue index 6970e0eb93..34a0d0faa8 100644 --- a/frontend/src/components/prompts/Move.vue +++ b/frontend/src/components/prompts/Move.vue @@ -98,6 +98,7 @@ export default { to: this.dest + encodeURIComponent(this.req.items[item].name), name: this.req.items[item].name, size: this.req.items[item].size, + isDir: this.req.items[item].isDir, modified: this.req.items[item].modified, overwrite: false, rename: false, @@ -122,7 +123,7 @@ export default { }); }; - const conflict = upload.checkConflict(items, this.dest); + const conflict = await upload.checkConflict(items, this.dest); if (conflict.length > 0) { this.showHover({ diff --git a/frontend/src/components/prompts/__tests__/copy-move-conflict.test.ts b/frontend/src/components/prompts/__tests__/copy-move-conflict.test.ts new file mode 100644 index 0000000000..8241be2e53 --- /dev/null +++ b/frontend/src/components/prompts/__tests__/copy-move-conflict.test.ts @@ -0,0 +1,125 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import CopyPrompt from "@/components/prompts/Copy.vue"; +import MovePrompt from "@/components/prompts/Move.vue"; +import { files as api } from "@/api"; +import { checkConflict } from "@/utils/upload"; + +vi.mock("@/api", () => ({ + files: { + copy: vi.fn().mockResolvedValue(undefined), + move: vi.fn().mockResolvedValue(undefined), + }, +})); + +vi.mock("@/api/utils", () => ({ + removePrefix: (value: string) => value.replace(/^\/files/, ""), +})); + +vi.mock("@/utils/buttons", () => ({ + default: { + loading: vi.fn(), + success: vi.fn(), + done: vi.fn(), + }, +})); + +vi.mock("@/utils/upload", () => ({ + checkConflict: vi.fn(), +})); + +const conflict = [ + { + index: 0, + name: "/target/file.txt", + origin: { size: 12 }, + dest: { size: 10 }, + checked: ["origin"], + isSmallerOnServer: true, + }, +]; + +function makeContext() { + return { + selected: [0], + req: { + items: [ + { + url: "/files/source/file.txt", + name: "file.txt", + size: 12, + isDir: false, + modified: "2026-06-04T00:00:00Z", + }, + ], + }, + dest: "/files/target/", + user: { redirectAfterCopyMove: false }, + $route: { path: "/files/source/" }, + $router: { push: vi.fn() }, + reload: false, + preselect: "", + showHover: vi.fn(), + closeHovers: vi.fn(), + $showError: vi.fn(), + }; +} + +const event = { + preventDefault: vi.fn(), +}; + +describe("copy and move conflict prompts", () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(checkConflict).mockResolvedValue(conflict); + }); + + it("waits for copy conflict detection before calling the copy API", async () => { + const context = makeContext(); + + await (CopyPrompt as any).methods.copy.call(context, event); + + expect(checkConflict).toHaveBeenCalledWith( + [ + expect.objectContaining({ + to: "/files/target/file.txt", + isDir: false, + }), + ], + "/files/target/" + ); + expect(context.showHover).toHaveBeenCalledWith( + expect.objectContaining({ + prompt: "resolve-conflict", + props: { conflict }, + }) + ); + expect(api.copy).not.toHaveBeenCalled(); + }); + + it("waits for move conflict detection before calling the move API", async () => { + const context = makeContext(); + + await (MovePrompt as any).methods.move.call(context, event); + + expect(checkConflict).toHaveBeenCalledWith( + [ + expect.objectContaining({ + to: "/files/target/file.txt", + isDir: false, + }), + ], + "/files/target/" + ); + expect(context.showHover).toHaveBeenCalledWith( + expect.objectContaining({ + prompt: "resolve-conflict", + props: expect.objectContaining({ + conflict, + files: expect.any(Array), + }), + }) + ); + expect(api.move).not.toHaveBeenCalled(); + }); +}); From 998bd95bfad1a9fbb81d214a77b168a393639718 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 4 Jun 2026 17:17:38 +0200 Subject: [PATCH 36/53] chore(release): 2.63.12 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8c5c235b7..9fd6149008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.63.12](https://github.com/filebrowser/filebrowser/compare/v2.63.11...v2.63.12) (2026-06-04) + + +### Bug Fixes + +* await copy move conflict detection ([#5978](https://github.com/filebrowser/filebrowser/issues/5978)) ([c1abe8f](https://github.com/filebrowser/filebrowser/commit/c1abe8f561208bf36bde70879d1a15ef9de998fa)) +* keep mobile file sort controls visible ([#5977](https://github.com/filebrowser/filebrowser/issues/5977)) ([0bb2768](https://github.com/filebrowser/filebrowser/commit/0bb2768754d11b865d68e72dcd7cebb232a6308a)) +* skip inaccessible children when listing directories ([#5958](https://github.com/filebrowser/filebrowser/issues/5958)) ([7b7ff8a](https://github.com/filebrowser/filebrowser/commit/7b7ff8ae8f97393b2e6ae6e061c1f780077c32b6)) + ## [2.63.11](https://github.com/filebrowser/filebrowser/compare/v2.63.10...v2.63.11) (2026-06-04) From 5f7311d32437e98d7c14c7b307a4f68109275535 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 6 Jun 2026 08:15:16 +0200 Subject: [PATCH 37/53] refactor: cleanup and simplify upload.ts --- .../utils/__tests__/check-conflict.test.ts | 180 ++++++++++++++++++ frontend/src/utils/upload.ts | 179 +++++------------ 2 files changed, 229 insertions(+), 130 deletions(-) create mode 100644 frontend/src/utils/__tests__/check-conflict.test.ts diff --git a/frontend/src/utils/__tests__/check-conflict.test.ts b/frontend/src/utils/__tests__/check-conflict.test.ts new file mode 100644 index 0000000000..e73bd979e0 --- /dev/null +++ b/frontend/src/utils/__tests__/check-conflict.test.ts @@ -0,0 +1,180 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { checkConflict } from "@/utils/upload"; +import { files as api } from "@/api"; + +vi.mock("@/api", () => ({ + files: { + fetchAll: vi.fn(), + }, +})); + +vi.mock("@/api/utils", () => ({ + removePrefix: (value: string) => value.replace(/^\/files/, ""), +})); + +// upload.ts imports these at module load; they reach window-bound constants +// which don't exist in the node test environment. checkConflict never uses +// them, so empty stubs keep the import graph from blowing up. +vi.mock("@/stores/layout", () => ({ useLayoutStore: vi.fn() })); +vi.mock("@/stores/upload", () => ({ useUploadStore: vi.fn() })); +vi.mock("@/utils/url", () => ({ default: {} })); + +// A move/copy/drag item carries `name` (raw) and `to` (URL-encoded) but no +// `fullPath` — mirroring what Move.vue / Copy.vue / ListingItem.vue build. +function moveItem(name: string, dest: string, size = 12) { + return { + from: `/files/source/${encodeURIComponent(name)}`, + to: dest + encodeURIComponent(name), + name, + size, + isDir: false, + overwrite: false, + rename: false, + }; +} + +describe("checkConflict", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("detects a conflict for a plain filename", async () => { + vi.mocked(api.fetchAll).mockResolvedValue([ + { + path: "/target/file.txt", + name: "file.txt", + size: 10, + modified: "2026-06-04T00:00:00Z", + isDir: false, + }, + ]); + + const conflicts = await checkConflict( + [moveItem("file.txt", "/files/target/")], + "/files/target/" + ); + + expect(conflicts).toHaveLength(1); + expect(conflicts[0].name).toBe("/target/file.txt"); + }); + + // Regression for #5957: names with encodable characters (spaces, "#", + // non-ASCII) were keyed by the URL-encoded `to` value and never matched the + // server's raw path, so the conflict modal was skipped and the backend + // returned a bare 409 instead. + it.each(["my file.txt", "résumé.pdf", "a#b.txt"])( + "detects a conflict for %s (encodable characters)", + async (name) => { + vi.mocked(api.fetchAll).mockResolvedValue([ + { + path: `/target/${name}`, + name, + size: 10, + modified: "2026-06-04T00:00:00Z", + isDir: false, + }, + ]); + + const conflicts = await checkConflict( + [moveItem(name, "/files/target/")], + "/files/target/" + ); + + expect(conflicts).toHaveLength(1); + expect(conflicts[0].name).toBe(`/target/${name}`); + } + ); + + it("reports no conflict when the destination has no matching name", async () => { + vi.mocked(api.fetchAll).mockResolvedValue([ + { + path: "/target/other.txt", + name: "other.txt", + size: 10, + modified: "2026-06-04T00:00:00Z", + isDir: false, + }, + ]); + + const conflicts = await checkConflict( + [moveItem("my file.txt", "/files/target/")], + "/files/target/" + ); + + expect(conflicts).toHaveLength(0); + }); + + it("detects nested conflicts for folder uploads via fullPath", async () => { + vi.mocked(api.fetchAll).mockResolvedValue([ + { + path: "/target/folder", + name: "folder", + size: 0, + modified: "2026-06-04T00:00:00Z", + isDir: true, + }, + { + path: "/target/folder/nested file.txt", + name: "nested file.txt", + size: 10, + modified: "2026-06-04T00:00:00Z", + isDir: false, + }, + ]); + + const files = [ + { name: "folder", size: 0, isDir: true, fullPath: "folder" }, + { + name: "nested file.txt", + size: 12, + isDir: false, + fullPath: "folder/nested file.txt", + }, + ]; + + const conflicts = await checkConflict(files, "/files/target/"); + + expect(conflicts).toHaveLength(1); + expect(conflicts[0].name).toBe("/target/folder/nested file.txt"); + }); + + // The "upload folder" file input pushes only files (with a relative + // fullPath) and no directory entries. Conflict detection must still find a + // nested file even though its parent folder is not in the upload list. + it("detects nested conflicts when no directory entries are present", async () => { + vi.mocked(api.fetchAll).mockResolvedValue([ + { + path: "/target/folder/deep/file.txt", + name: "file.txt", + size: 10, + modified: "2026-06-04T00:00:00Z", + isDir: false, + }, + ]); + + const files = [ + { + name: "file.txt", + size: 12, + isDir: false, + fullPath: "folder/deep/file.txt", + }, + ]; + + const conflicts = await checkConflict(files, "/files/target/"); + + expect(conflicts).toHaveLength(1); + expect(conflicts[0].name).toBe("/target/folder/deep/file.txt"); + }); + + it("returns no conflicts when the recursive listing fails", async () => { + vi.mocked(api.fetchAll).mockRejectedValue(new Error("404")); + + const conflicts = await checkConflict( + [moveItem("file.txt", "/files/target/")], + "/files/target/" + ); + + expect(conflicts).toHaveLength(0); + }); +}); diff --git a/frontend/src/utils/upload.ts b/frontend/src/utils/upload.ts index 060f5d94d8..994c95be59 100644 --- a/frontend/src/utils/upload.ts +++ b/frontend/src/utils/upload.ts @@ -4,159 +4,78 @@ import url from "@/utils/url"; import { files as api } from "@/api"; import { removePrefix } from "@/api/utils"; -interface UploadEntryWithChild extends UploadEntry { - children?: UploadEntryWithChild[]; - originalIndex: number; -} - /** - * Convert UploadList into a forest (array of root nodes). - * This properly handles uploads with multiple top-level files/folders - * instead of assuming a single root. - * @param flatArray - * @param basePath + * The path used to detect conflicts against the server's recursive listing. + * + * It MUST be the raw, un-encoded path relative to the destination: + * - `fullPath` is set for folder uploads and drag & drop (e.g. "sub/file.txt"). + * - `name` is the leaf for every other case (copy/move/paste/single upload), + * which is always a flat top-level entry. + * + * We never key on `item.to`: it is URL-encoded (`dest + encodeURIComponent(name)`) + * and would miss conflicts for any name with encodable characters (spaces, "#", + * non-ASCII, ...), surfacing a raw 409 error instead of the conflict modal. + * @param item */ -function flatToForest( - flatArray: UploadList, - basePath: string -): UploadEntryWithChild[] { - const nodeMap: Record = {}; - - // First pass: create all nodes - flatArray.forEach((item, index) => { - // File list is created from very different action and info available are not always the same. - // By doing a drag and drop or upload a folder (both from the browser or from the OS) we have the fullPath property available - // By uploading a single file using the file input, we only have the "name" property - // By doing drag and drop from filebrowser to filebrowser, we have the "to" property available but not the fullPath - const fullPathOrTo = - item.fullPath || item.to?.replace(basePath, "") || item.name; - nodeMap[fullPathOrTo!] = { - fullPath: fullPathOrTo, - isDir: item.isDir, - name: item.name, - size: item.size, - originalIndex: index, - ...(item.isDir && { children: [] }), - ...(item.file && { file: item.file }), - }; - }); - - const roots: UploadEntryWithChild[] = []; - - // Second pass: build hierarchy - flatArray.forEach((item) => { - // see comment before to explanation - const fullPathOrTo = - item.fullPath || item.to?.replace(basePath, "") || item.name; - - const node = nodeMap[fullPathOrTo!]; - const lastSlash = fullPathOrTo!.lastIndexOf("/"); - - if (lastSlash === -1) { - roots.push(node); - } else { - const parentPath = fullPathOrTo!.substring(0, lastSlash); - const parent = nodeMap[parentPath]; - if (parent?.children) { - parent.children.push(node); - } - } - }); - - return roots; +function conflictKey(item: UploadEntry): string { + return (item.fullPath || item.name).replace(/^\/+/, ""); } /** - * Return conflict files from - * @param files - flat upload list to check - * @param basePath - server destination path (e.g. "/files/uploads/") + * Return the entries from `files` that already exist under `basePath` on the + * server, so the caller can prompt the user to overwrite/rename/skip. + * + * The whole destination tree is fetched once and indexed by path relative to + * the destination, then every file is looked up directly — no need to mirror + * the upload's folder structure. Directories are never reported: an existing + * folder is merged on upload, and a copy/move onto one is rejected server-side. + * + * @param files - flat upload list to check + * @param basePath - server destination path (e.g. "/files/uploads/") */ export async function checkConflict( files: UploadList, basePath: string ): Promise { - console.log( - "Starting conflict check, " + files.length + " possible conflict found." - ); - - const forest = flatToForest(files, basePath); - if (forest.length === 0) return []; + if (files.length === 0) return []; - // Single API call: fetch the entire server tree under basePath. - let serverEntries: RecursiveEntry[] = []; + let serverEntries: RecursiveEntry[]; try { + // Single API call: fetch the entire server tree under basePath. serverEntries = await api.fetchAll(basePath); } catch { - console.error( - `Failed to fetch recursive server listing for ${basePath}. ` + - `Assuming directory doesn't exist and skipping conflict check.` - ); + // The destination doesn't exist yet, so nothing can conflict. return []; } - // Build a lookup map keyed by the normalised server path for O(1) access. - // The server returns paths relative to the user's scope (e.g. "/uploads/foo.txt"). - // We strip the basePath prefix so the key matches the upload entry's fullPath. + // The server returns paths absolute within the user's scope + // (e.g. "/uploads/sub/file.txt"). Strip the basePath prefix so the keys line + // up with each entry's conflictKey, which is relative to the destination. const normBase = removePrefix(basePath).replace(/\/+$/, ""); const serverMap = new Map(); for (const entry of serverEntries) { - // entry.path is absolute from server root, e.g. "/uploads/sub/file.txt" - // We need the relative part after normBase, e.g. "sub/file.txt" - let rel = entry.path; - if (rel.startsWith(normBase)) { - rel = rel.slice(normBase.length); - } - // Strip leading slash so it matches fullPath format ("sub/file.txt") - rel = rel.replace(/^\/+/, ""); - serverMap.set(rel, entry); + const rel = entry.path.startsWith(normBase) + ? entry.path.slice(normBase.length) + : entry.path; + serverMap.set(rel.replace(/^\/+/, ""), entry); } const conflicts: ConflictingResource[] = []; - - /** - * Walk the upload tree and compare each file node against the - * pre-fetched server map. Directories only need to be recursed - * when they appear in the map (otherwise no child can conflict). - */ - function recursiveCheckConflict(nodes: UploadEntryWithChild[]): void { - for (const node of nodes) { - if (node.isDir && node.children) { - // Only recurse if this directory exists on the server - const dirKey = node.fullPath!.replace(/^\/+/, ""); - if (serverMap.has(dirKey)) { - recursiveCheckConflict(node.children); - } - } else { - // File – check for a conflict against the server map - const fileKey = node.fullPath!.replace(/^\/+/, ""); - const serverEntry = serverMap.get(fileKey); - - if (serverEntry) { - conflicts.push({ - index: node.originalIndex, - name: serverEntry.path, - origin: { - lastModified: node.file?.lastModified, - size: node.size, - }, - dest: { - lastModified: serverEntry.modified, - size: serverEntry.size, - }, - checked: ["origin"], - isSmallerOnServer: node.size > serverEntry.size, - }); - } - } - } - } - - // Walk all root nodes synchronously against the pre-fetched data - recursiveCheckConflict(forest); - - console.log(conflicts.length + " conflicts found."); - - conflicts.sort((a, b) => a.index - b.index); + files.forEach((file, index) => { + if (file.isDir) return; // see directory note above + + const server = serverMap.get(conflictKey(file)); + if (!server) return; + + conflicts.push({ + index, + name: server.path, + origin: { lastModified: file.file?.lastModified, size: file.size }, + dest: { lastModified: server.modified, size: server.size }, + checked: ["origin"], + isSmallerOnServer: file.size > server.size, + }); + }); return conflicts; } From a1a514dcbb216d2080412c5354eea1e1fb033050 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 6 Jun 2026 08:28:31 +0200 Subject: [PATCH 38/53] fix: copy/move allow overwrite --- frontend/src/components/files/ListingItem.vue | 3 +- frontend/src/components/prompts/Copy.vue | 2 +- frontend/src/components/prompts/Move.vue | 2 +- .../__tests__/copy-move-conflict.test.ts | 6 ++- frontend/src/types/file.d.ts | 1 + .../utils/__tests__/check-conflict.test.ts | 43 +++++++++++++++++++ frontend/src/utils/upload.ts | 22 +++++++--- frontend/src/views/files/FileListing.vue | 4 +- 8 files changed, 70 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/files/ListingItem.vue b/frontend/src/components/files/ListingItem.vue index d6747f59f2..33b5a99358 100644 --- a/frontend/src/components/files/ListingItem.vue +++ b/frontend/src/components/files/ListingItem.vue @@ -179,6 +179,7 @@ const drop = async (event: Event) => { to: props.url + encodeURIComponent(fileStore.req?.items[i].name), name: fileStore.req?.items[i].name, size: fileStore.req?.items[i].size, + isDir: fileStore.req?.items[i].isDir, modified: fileStore.req?.items[i].modified, overwrite: false, rename: false, @@ -204,7 +205,7 @@ const drop = async (event: Event) => { .catch($showError); }; - const conflict = await upload.checkConflict(items, path); + const conflict = await upload.checkConflict(items, path, true); if (conflict.length > 0) { layoutStore.showHover({ diff --git a/frontend/src/components/prompts/Copy.vue b/frontend/src/components/prompts/Copy.vue index 0073df8dcb..37139b9bc2 100644 --- a/frontend/src/components/prompts/Copy.vue +++ b/frontend/src/components/prompts/Copy.vue @@ -123,7 +123,7 @@ export default { }); }; - const conflict = await upload.checkConflict(items, this.dest); + const conflict = await upload.checkConflict(items, this.dest, true); if (conflict.length > 0) { this.showHover({ diff --git a/frontend/src/components/prompts/Move.vue b/frontend/src/components/prompts/Move.vue index 34a0d0faa8..b8086a67f1 100644 --- a/frontend/src/components/prompts/Move.vue +++ b/frontend/src/components/prompts/Move.vue @@ -123,7 +123,7 @@ export default { }); }; - const conflict = await upload.checkConflict(items, this.dest); + const conflict = await upload.checkConflict(items, this.dest, true); if (conflict.length > 0) { this.showHover({ diff --git a/frontend/src/components/prompts/__tests__/copy-move-conflict.test.ts b/frontend/src/components/prompts/__tests__/copy-move-conflict.test.ts index 8241be2e53..cc138329ed 100644 --- a/frontend/src/components/prompts/__tests__/copy-move-conflict.test.ts +++ b/frontend/src/components/prompts/__tests__/copy-move-conflict.test.ts @@ -86,7 +86,8 @@ describe("copy and move conflict prompts", () => { isDir: false, }), ], - "/files/target/" + "/files/target/", + true ); expect(context.showHover).toHaveBeenCalledWith( expect.objectContaining({ @@ -109,7 +110,8 @@ describe("copy and move conflict prompts", () => { isDir: false, }), ], - "/files/target/" + "/files/target/", + true ); expect(context.showHover).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/frontend/src/types/file.d.ts b/frontend/src/types/file.d.ts index 7e6a9ebc03..067598b04a 100644 --- a/frontend/src/types/file.d.ts +++ b/frontend/src/types/file.d.ts @@ -53,6 +53,7 @@ interface ClipItem { from: string; name: string; size?: number; + isDir?: boolean; modified?: string; } diff --git a/frontend/src/utils/__tests__/check-conflict.test.ts b/frontend/src/utils/__tests__/check-conflict.test.ts index e73bd979e0..37a4e69b7e 100644 --- a/frontend/src/utils/__tests__/check-conflict.test.ts +++ b/frontend/src/utils/__tests__/check-conflict.test.ts @@ -167,6 +167,49 @@ describe("checkConflict", () => { expect(conflicts[0].name).toBe("/target/folder/deep/file.txt"); }); + // Copy/move stats the whole destination, so a same-named directory is a + // conflict in its own right (regression for the directory case of #5957). + it("reports a directory conflict for copy/move (includeDirectories)", async () => { + vi.mocked(api.fetchAll).mockResolvedValue([ + { + path: "/target/folder", + name: "folder", + size: 0, + modified: "2026-06-04T00:00:00Z", + isDir: true, + }, + ]); + + const items = [{ ...moveItem("folder", "/files/target/", 0), isDir: true }]; + + const conflicts = await checkConflict(items, "/files/target/", true); + + expect(conflicts).toHaveLength(1); + expect(conflicts[0].name).toBe("/target/folder"); + }); + + // Uploads merge into an existing folder, so the directory itself must not be + // reported — only the files inside it can conflict. + it("ignores a directory conflict for uploads (default)", async () => { + vi.mocked(api.fetchAll).mockResolvedValue([ + { + path: "/target/folder", + name: "folder", + size: 0, + modified: "2026-06-04T00:00:00Z", + isDir: true, + }, + ]); + + const files = [ + { name: "folder", size: 0, isDir: true, fullPath: "folder" }, + ]; + + const conflicts = await checkConflict(files, "/files/target/"); + + expect(conflicts).toHaveLength(0); + }); + it("returns no conflicts when the recursive listing fails", async () => { vi.mocked(api.fetchAll).mockRejectedValue(new Error("404")); diff --git a/frontend/src/utils/upload.ts b/frontend/src/utils/upload.ts index 994c95be59..c4a9aab47e 100644 --- a/frontend/src/utils/upload.ts +++ b/frontend/src/utils/upload.ts @@ -26,16 +26,24 @@ function conflictKey(item: UploadEntry): string { * server, so the caller can prompt the user to overwrite/rename/skip. * * The whole destination tree is fetched once and indexed by path relative to - * the destination, then every file is looked up directly — no need to mirror - * the upload's folder structure. Directories are never reported: an existing - * folder is merged on upload, and a copy/move onto one is rejected server-side. + * the destination, then every entry is looked up directly — no need to mirror + * the upload's folder structure. * - * @param files - flat upload list to check - * @param basePath - server destination path (e.g. "/files/uploads/") + * Directory handling differs by action, hence `includeDirectories`: + * - Upload (false): an existing folder is silently merged, so only the + * individual files inside it can conflict. + * - Copy/move (true): the server stats the destination and rejects it whole if + * a same-named entry exists, so the directory itself is a conflict. The list + * only holds the top-level items being moved, so each is reported once. + * + * @param files - flat upload list to check + * @param basePath - server destination path (e.g. "/files/uploads/") + * @param includeDirectories - report directories as conflicts (copy/move) */ export async function checkConflict( files: UploadList, - basePath: string + basePath: string, + includeDirectories = false ): Promise { if (files.length === 0) return []; @@ -62,7 +70,7 @@ export async function checkConflict( const conflicts: ConflictingResource[] = []; files.forEach((file, index) => { - if (file.isDir) return; // see directory note above + if (file.isDir && !includeDirectories) return; // see directory note above const server = serverMap.get(conflictKey(file)); if (!server) return; diff --git a/frontend/src/views/files/FileListing.vue b/frontend/src/views/files/FileListing.vue index 06a010e498..9bf4c48833 100644 --- a/frontend/src/views/files/FileListing.vue +++ b/frontend/src/views/files/FileListing.vue @@ -632,6 +632,7 @@ const copyCut = (event: Event | KeyboardEvent): void => { from: fileStore.req.items[i].url, name: fileStore.req.items[i].name, size: fileStore.req.items[i].size, + isDir: fileStore.req.items[i].isDir, modified: fileStore.req.items[i].modified, }); } @@ -661,6 +662,7 @@ const paste = async (event: Event) => { to, name: item.name, size: item.size, + isDir: item.isDir, modified: item.modified, overwrite: false, rename: clipboardStore.path == route.path, @@ -697,7 +699,7 @@ const paste = async (event: Event) => { } const path = route.path.endsWith("/") ? route.path : route.path + "/"; - const conflict = await upload.checkConflict(items, path); + const conflict = await upload.checkConflict(items, path, true); if (conflict.length > 0) { layoutStore.showHover({ From 67ed670d92966e0c8b57d7b726572be6b905fe8a Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 6 Jun 2026 08:33:27 +0200 Subject: [PATCH 39/53] chore(release): 2.63.13 --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fd6149008..3129149f2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.63.13](https://github.com/filebrowser/filebrowser/compare/v2.63.12...v2.63.13) (2026-06-06) + + +### Bug Fixes + +* copy/move allow overwrite ([a1a514d](https://github.com/filebrowser/filebrowser/commit/a1a514dcbb216d2080412c5354eea1e1fb033050)) + + +### Refactorings + +* cleanup and simplify upload.ts ([5f7311d](https://github.com/filebrowser/filebrowser/commit/5f7311d32437e98d7c14c7b307a4f68109275535)) + ## [2.63.12](https://github.com/filebrowser/filebrowser/compare/v2.63.11...v2.63.12) (2026-06-04) From 3406d3d7f98dfc3c16e4ff7ff4a87e3bdfe221dd Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 7 Jun 2026 17:12:31 +0200 Subject: [PATCH 40/53] fix: recursive check --- files/file.go | 4 --- fileutils/copy_test.go | 74 ++++++++++++++++++++++++++++++++++++++++++ fileutils/dir.go | 10 ++++++ fileutils/file.go | 9 +++++ http/resource.go | 7 ---- http/tus_handlers.go | 3 +- 6 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 fileutils/copy_test.go diff --git a/files/file.go b/files/file.go index e84c7e2aa5..88990feb0c 100644 --- a/files/file.go +++ b/files/file.go @@ -475,10 +475,6 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool, calcImgRe isSymlink, isInvalidLink := false, false if IsSymlink(f.Mode()) { isSymlink = true - // A symlink whose on-disk target escapes the scoped root must not be - // followed, otherwise the listing would leak the target's metadata - // (and downstream access) for files outside the user's scope or the - // shared subtree. if ok, scopeErr := WithinScope(i.Fs, fPath); scopeErr != nil || !ok { continue } diff --git a/fileutils/copy_test.go b/fileutils/copy_test.go new file mode 100644 index 0000000000..f9cad6f61e --- /dev/null +++ b/fileutils/copy_test.go @@ -0,0 +1,74 @@ +package fileutils + +import ( + "os" + "path/filepath" + "testing" + + "github.com/spf13/afero" +) + +// Copying an in-scope directory that contains a symlink whose target escapes +// the user's scope must not dereference that symlink into the destination. +// Otherwise a scoped user could exfiltrate out-of-scope file content via the +// recursive copy path (GHSA-c2gv-wf5f-hjhh, an incomplete fix of +// GHSA-239w-m3h6-ch8v). +func TestCopyDoesNotDereferenceEscapingSymlink(t *testing.T) { + base := t.TempDir() + scope := filepath.Join(base, "scope") + if err := os.MkdirAll(filepath.Join(scope, "srcdir"), 0o755); err != nil { + t.Fatal(err) + } + + // A secret living outside the scope. + secret := filepath.Join(base, "secret.txt") + if err := os.WriteFile(secret, []byte("OUT-OF-SCOPE-SECRET"), 0o644); err != nil { + t.Fatal(err) + } + + // An escaping symlink planted inside the user's scope. + if err := os.Symlink(secret, filepath.Join(scope, "srcdir", "link.txt")); err != nil { + t.Skipf("cannot create symlink: %v", err) + } + + afs := afero.NewBasePathFs(afero.NewOsFs(), scope) + + err := Copy(afs, "/srcdir", "/dstdir", 0o644, 0o755) + if err == nil { + t.Fatal("expected copy of a directory containing an escaping symlink to fail") + } + + // The escaping symlink's target content must not have landed in scope. + if data, readErr := afero.ReadFile(afs, "/dstdir/link.txt"); readErr == nil { + t.Fatalf("escaping symlink was dereferenced into scope: got %q", string(data)) + } +} + +// A symlink whose target stays within scope is legitimate and must still be +// copied (dereferenced) so the fix does not over-block normal usage. +func TestCopyAllowsInScopeSymlink(t *testing.T) { + scope := t.TempDir() + if err := os.MkdirAll(filepath.Join(scope, "srcdir", "real"), 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(scope, "srcdir", "real", "f.txt"), []byte("in-scope"), 0o644); err != nil { + t.Fatal(err) + } + if err := os.Symlink(filepath.Join(scope, "srcdir", "real", "f.txt"), filepath.Join(scope, "srcdir", "link.txt")); err != nil { + t.Skipf("cannot create symlink: %v", err) + } + + afs := afero.NewBasePathFs(afero.NewOsFs(), scope) + + if err := Copy(afs, "/srcdir", "/dstdir", 0o644, 0o755); err != nil { + t.Fatalf("expected copy of an in-scope symlink to succeed, got: %v", err) + } + + data, err := afero.ReadFile(afs, "/dstdir/link.txt") + if err != nil { + t.Fatalf("expected in-scope symlink to be copied, got: %v", err) + } + if string(data) != "in-scope" { + t.Fatalf("unexpected copied content: %q", string(data)) + } +} diff --git a/fileutils/dir.go b/fileutils/dir.go index e0b049db40..0b127c482c 100644 --- a/fileutils/dir.go +++ b/fileutils/dir.go @@ -3,14 +3,24 @@ package fileutils import ( "errors" "io/fs" + "os" "github.com/spf13/afero" + + "github.com/filebrowser/filebrowser/v2/files" ) // CopyDir copies a directory from source to dest and all // of its sub-directories. It doesn't stop if it finds an error // during the copy. Returns an error if any. func CopyDir(afs afero.Fs, source, dest string, fileMode, dirMode fs.FileMode) error { + if ok, err := files.WithinScope(afs, source); err != nil || !ok { + if err != nil { + return err + } + return os.ErrPermission + } + // Get properties of source. srcinfo, err := afs.Stat(source) if err != nil { diff --git a/fileutils/file.go b/fileutils/file.go index 784f728f65..ab8c7fff3f 100644 --- a/fileutils/file.go +++ b/fileutils/file.go @@ -8,6 +8,8 @@ import ( "path/filepath" "github.com/spf13/afero" + + "github.com/filebrowser/filebrowser/v2/files" ) // MoveFile moves file from src to dst. @@ -32,6 +34,13 @@ func MoveFile(afs afero.Fs, src, dst string, fileMode, dirMode fs.FileMode) erro // CopyFile copies a file from source to dest and returns // an error if any. func CopyFile(afs afero.Fs, source, dest string, fileMode, dirMode fs.FileMode) error { + if ok, err := files.WithinScope(afs, source); err != nil || !ok { + if err != nil { + return err + } + return os.ErrPermission + } + // Open the source file. src, err := afs.Open(source) if err != nil { diff --git a/http/resource.go b/http/resource.go index 5f06a7cdfb..27eb8b70f2 100644 --- a/http/resource.go +++ b/http/resource.go @@ -228,10 +228,6 @@ func resourcePatchHandler(fileCache FileCache) handleFunc { return http.StatusForbidden, nil } - // Refuse to copy/move through a symlink that escapes the user's scope, - // for either the source (read escape) or the destination (write - // escape). fileutils.Copy/MoveFile operate on the raw afero FS and - // follow symlinks, so they bypass the guards in stat()/writeFile(). for _, p := range []string{src, dst} { if ok, scopeErr := files.WithinScope(d.user.Fs, p); scopeErr != nil || !ok { if scopeErr != nil { @@ -308,9 +304,6 @@ func addVersionSuffix(source string, afs afero.Fs) string { } func writeFile(afs afero.Fs, dst string, in io.Reader, fileMode, dirMode fs.FileMode) (os.FileInfo, error) { - // Refuse to write through a symlink that escapes the user's scope, so an - // overwrite of an existing escaping symlink cannot modify a file outside - // the boundary. if ok, err := files.WithinScope(afs, dst); err != nil || !ok { return nil, os.ErrPermission } diff --git a/http/tus_handlers.go b/http/tus_handlers.go index 8a68161d12..2629bd09f0 100644 --- a/http/tus_handlers.go +++ b/http/tus_handlers.go @@ -166,8 +166,7 @@ func tusPatchHandler(cache UploadCache) handleFunc { if r.Header.Get("Content-Type") != "application/offset+octet-stream" { return http.StatusUnsupportedMediaType, nil } - // Defense in depth: refuse to write through a symlink that escapes the - // scope, in case the target path is (or sits behind) an escaping link. + if ok, scopeErr := files.WithinScope(d.user.Fs, r.URL.Path); scopeErr != nil || !ok { return http.StatusForbidden, nil } From 7c2c0a11b31b2bb214d741005a0b02b1764208b3 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 7 Jun 2026 18:16:46 +0200 Subject: [PATCH 41/53] refactor: ScopedFs to avoid escaping symlinks --- files/file.go | 82 +++------------- files/file_test.go | 70 ++++++-------- files/scoped.go | 184 ++++++++++++++++++++++++++++++++++++ fileutils/copy_test.go | 5 +- fileutils/dir.go | 10 -- fileutils/file.go | 9 -- http/public.go | 11 ++- http/public_symlink_test.go | 6 +- http/public_test.go | 10 +- http/raw.go | 7 +- http/resource.go | 18 +--- http/share.go | 18 +--- http/tus_handlers.go | 11 +-- http/tus_symlink_test.go | 3 +- users/users.go | 41 ++++---- 15 files changed, 276 insertions(+), 209 deletions(-) create mode 100644 files/scoped.go diff --git a/files/file.go b/files/file.go index 88990feb0c..4102c3c4c5 100644 --- a/files/file.go +++ b/files/file.go @@ -22,10 +22,9 @@ import ( "strings" "time" - "github.com/spf13/afero" - fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/rules" + "github.com/spf13/afero" ) var ( @@ -129,13 +128,6 @@ func stat(opts *FileOptions) (*FileInfo, error) { } } - if file != nil { - ok, scopeErr := WithinScope(opts.Fs, opts.Path) - if scopeErr != nil || !ok { - return nil, os.ErrPermission - } - } - // regular file if file != nil && !file.IsSymlink { return file, nil @@ -173,60 +165,6 @@ func stat(opts *FileOptions) (*FileInfo, error) { return file, nil } -// WithinScope reports whether the on-disk target of p — after resolving any -// symbolic links — stays within the scoped root of fsys. It exists to stop a -// symlink that lives lexically inside a user's scope but points outside it -// from being followed for reads, writes, or shares. -// -// Paths that do not exist yet (e.g. a brand-new file being created) are -// validated against their nearest existing ancestor, so legitimate new files -// are always allowed. For a filesystem that is not scoped with BasePathFs the -// check is a no-op and returns true. -// -// Note: a dangling symlink whose target does not yet exist resolves to its -// containing directory and is therefore allowed; writing through such a link -// could still create a file outside the scope. Callers that create files -// should treat this as best-effort and rely on rejecting existing escaping -// symlinks, which covers the disclosure and overwrite vectors. -func WithinScope(fsys afero.Fs, p string) (bool, error) { - bfs, ok := fsys.(*afero.BasePathFs) - if !ok { - // Not a scoped filesystem; nothing to enforce. - return true, nil - } - - root, err := filepath.EvalSymlinks(afero.FullBaseFsPath(bfs, "/")) - if err != nil { - return false, err - } - - target := afero.FullBaseFsPath(bfs, p) - resolved, err := filepath.EvalSymlinks(target) - for errors.Is(err, fs.ErrNotExist) { - parent := filepath.Dir(target) - if parent == target { - break - } - target = parent - resolved, err = filepath.EvalSymlinks(target) - } - if err != nil { - return false, err - } - - // Compare against root with a trailing separator so a sibling like - // "/srvother" is not treated as being inside "/srv". When root is itself - // the filesystem boundary (e.g. "/"), it already ends in a separator, so - // avoid producing "//" — which no path would match — and accept any path - // under it. - prefix := root - if !strings.HasSuffix(prefix, string(filepath.Separator)) { - prefix += string(filepath.Separator) - } - - return resolved == root || strings.HasPrefix(resolved, prefix), nil -} - // Checksum checksums a given File for a given User, using a specific // algorithm. The checksums data is saved on File object. func (i *FileInfo) Checksum(algo string) error { @@ -475,15 +413,19 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool, calcImgRe isSymlink, isInvalidLink := false, false if IsSymlink(f.Mode()) { isSymlink = true - if ok, scopeErr := WithinScope(i.Fs, fPath); scopeErr != nil || !ok { - continue - } - // It's a symbolic link. We try to follow it. If it doesn't work, - // we stay with the link information instead of the target's. + // It's a symbolic link. We try to follow it. The scoped filesystem + // refuses to dereference a link whose target escapes the scope + // (permission error); such a link is omitted from the listing + // entirely so it cannot leak the target's metadata. Any other + // failure means a broken link, which we surface as an invalid link + // rather than the target's information. info, err := i.Fs.Stat(fPath) - if err == nil { + switch { + case err == nil: f = info - } else { + case errors.Is(err, os.ErrPermission): + continue + default: isInvalidLink = true } } diff --git a/files/file_test.go b/files/file_test.go index 9b46a85dbd..1efda7f601 100644 --- a/files/file_test.go +++ b/files/file_test.go @@ -9,54 +9,46 @@ import ( "github.com/spf13/afero" ) -func TestWithinScope(t *testing.T) { - t.Run("non-scoped filesystem is a no-op", func(t *testing.T) { - ok, err := WithinScope(afero.NewOsFs(), "/anything") - if err != nil || !ok { - t.Fatalf("expected (true, nil), got (%v, %v)", ok, err) - } - }) - - t.Run("path inside a nested scope is allowed", func(t *testing.T) { +func TestScopedFs(t *testing.T) { + t.Run("path inside scope is allowed", func(t *testing.T) { scope := t.TempDir() if err := os.WriteFile(filepath.Join(scope, "file.txt"), []byte("x"), 0o644); err != nil { t.Fatal(err) } - bfs := afero.NewBasePathFs(afero.NewOsFs(), scope) + fs := NewScopedFs(afero.NewOsFs(), scope) - ok, err := WithinScope(bfs, "/file.txt") - if err != nil || !ok { - t.Fatalf("expected (true, nil), got (%v, %v)", ok, err) + if _, err := fs.Stat("/file.txt"); err != nil { + t.Fatalf("expected in-scope file to be accessible, got %v", err) } }) - t.Run("new file inside scope is allowed", func(t *testing.T) { + t.Run("new file inside scope can be created", func(t *testing.T) { scope := t.TempDir() - bfs := afero.NewBasePathFs(afero.NewOsFs(), scope) + fs := NewScopedFs(afero.NewOsFs(), scope) - ok, err := WithinScope(bfs, "/does-not-exist-yet.txt") - if err != nil || !ok { - t.Fatalf("expected (true, nil), got (%v, %v)", ok, err) + f, err := fs.OpenFile("/does-not-exist-yet.txt", os.O_RDWR|os.O_CREATE, 0o644) + if err != nil { + t.Fatalf("expected to create a new in-scope file, got %v", err) } + _ = f.Close() }) // Regression for #5975: when the scope resolves to the filesystem root, // root+separator used to be "//", which no path matched, so every write // was rejected with os.ErrPermission (HTTP 403). - t.Run("filesystem root scope allows writes", func(t *testing.T) { + t.Run("filesystem root scope allows access", func(t *testing.T) { f := filepath.Join(t.TempDir(), "file.txt") if err := os.WriteFile(f, []byte("x"), 0o644); err != nil { t.Fatal(err) } - bfs := afero.NewBasePathFs(afero.NewOsFs(), "/") + fs := NewScopedFs(afero.NewOsFs(), "/") - ok, err := WithinScope(bfs, f) - if err != nil || !ok { - t.Fatalf("expected (true, nil) for a path under root scope, got (%v, %v)", ok, err) + if _, err := fs.Stat(f); err != nil { + t.Fatalf("expected a path under root scope to be accessible, got %v", err) } }) - t.Run("sibling of a nested scope is rejected", func(t *testing.T) { + t.Run("escaping symlink to a sibling is rejected", func(t *testing.T) { base := t.TempDir() scope := filepath.Join(base, "srv") sibling := filepath.Join(base, "srvother") @@ -65,20 +57,21 @@ func TestWithinScope(t *testing.T) { t.Fatal(err) } } - // A symlink lexically inside the scope pointing at a sibling directory - // must not be followed. - link := filepath.Join(scope, "escape") - if err := os.Symlink(sibling, link); err != nil { + if err := os.WriteFile(filepath.Join(sibling, "secret.txt"), []byte("secret"), 0o644); err != nil { t.Fatal(err) } - bfs := afero.NewBasePathFs(afero.NewOsFs(), scope) + // A symlink lexically inside the scope pointing at a sibling directory + // must not be followed for reads or stats. + if err := os.Symlink(sibling, filepath.Join(scope, "escape")); err != nil { + t.Skipf("cannot create symlink: %v", err) + } + fs := NewScopedFs(afero.NewOsFs(), scope) - ok, err := WithinScope(bfs, "/escape") - if err != nil { - t.Fatalf("unexpected error: %v", err) + if _, err := fs.Stat("/escape"); !os.IsPermission(err) { + t.Fatalf("expected stat of escaping symlink to be rejected, got %v", err) } - if ok { - t.Fatal("expected escaping symlink to a sibling directory to be rejected") + if _, err := fs.Open("/escape/secret.txt"); !os.IsPermission(err) { + t.Fatalf("expected read through escaping symlink to be rejected, got %v", err) } }) @@ -93,11 +86,10 @@ func TestWithinScope(t *testing.T) { if err := os.Symlink(filepath.Join(scope, "real"), filepath.Join(scope, "link")); err != nil { t.Skipf("cannot create symlink: %v", err) } - bfs := afero.NewBasePathFs(afero.NewOsFs(), scope) + fs := NewScopedFs(afero.NewOsFs(), scope) - ok, err := WithinScope(bfs, "/link/f.txt") - if err != nil || !ok { - t.Fatalf("expected (true, nil) for an in-scope symlink target, got (%v, %v)", ok, err) + if _, err := fs.Stat("/link/f.txt"); err != nil { + t.Fatalf("expected in-scope symlink target to be accessible, got %v", err) } }) } @@ -123,7 +115,7 @@ func TestStatRejectsLinkedAncestorEscape(t *testing.T) { } // Filesystem scoped to the shared directory, as a public share would be. - bfs := afero.NewBasePathFs(afero.NewOsFs(), filepath.Join(scope, "shared")) + bfs := NewScopedFs(afero.NewOsFs(), filepath.Join(scope, "shared")) if _, err := stat(&FileOptions{Fs: bfs, Path: "/link/secret.txt"}); !os.IsPermission(err) { t.Fatalf("expected permission error for linked-ancestor escape, got %v", err) diff --git a/files/scoped.go b/files/scoped.go new file mode 100644 index 0000000000..e6e1843512 --- /dev/null +++ b/files/scoped.go @@ -0,0 +1,184 @@ +package files + +import ( + "errors" + "io/fs" + "os" + "path/filepath" + "strings" + "time" + + "github.com/spf13/afero" +) + +// ScopedFs is an afero.Fs that confines every operation to a base directory and +// refuses to follow a symbolic link whose on-disk target resolves outside that +// base. It wraps an *afero.BasePathFs — which already provides the lexical +// confinement — and adds a per-operation scope check on every call that would +// dereference a symlink at the OS layer (open, stat, lstat, chmod, …). +type ScopedFs struct { + base *afero.BasePathFs +} + +var ( + _ afero.Fs = (*ScopedFs)(nil) + _ afero.Lstater = (*ScopedFs)(nil) +) + +func NewScopedFs(source afero.Fs, path string) *ScopedFs { + if s, ok := source.(*ScopedFs); ok { + source = s.base + } + return &ScopedFs{base: afero.NewBasePathFs(source, path).(*afero.BasePathFs)} +} + +// Base returns the underlying *afero.BasePathFs. +func (s *ScopedFs) Base() *afero.BasePathFs { return s.base } + +// guard returns an error if name's on-disk target resolves outside the scope. +func (s *ScopedFs) guard(name string) error { + ok, err := s.within(name) + if err != nil { + return err + } + if !ok { + return os.ErrPermission + } + return nil +} + +// within reports whether the on-disk target of p — after resolving any symbolic +// links — stays within the scoped root. It exists to stop a symlink that lives +// lexically inside the scope but points outside it from being followed for +// reads, writes, or shares. +// +// Paths that do not exist yet (e.g. a brand-new file being created) are +// validated against their nearest existing ancestor, so legitimate new files +// are always allowed. +// +// Note: a dangling symlink whose target does not yet exist resolves to its +// containing directory and is therefore allowed; writing through such a link +// could still create a file outside the scope. This is treated as best-effort +// and relies on rejecting existing escaping symlinks, which covers the +// disclosure and overwrite vectors. +func (s *ScopedFs) within(p string) (bool, error) { + root, err := filepath.EvalSymlinks(afero.FullBaseFsPath(s.base, "/")) + if err != nil { + return false, err + } + + target := afero.FullBaseFsPath(s.base, p) + resolved, err := filepath.EvalSymlinks(target) + for errors.Is(err, fs.ErrNotExist) { + parent := filepath.Dir(target) + if parent == target { + break + } + target = parent + resolved, err = filepath.EvalSymlinks(target) + } + if err != nil { + return false, err + } + + // Compare against root with a trailing separator so a sibling like + // "/srvother" is not treated as being inside "/srv". When root is itself the + // filesystem boundary (e.g. "/"), it already ends in a separator, so avoid + // producing "//" — which no path would match — and accept any path under it. + prefix := root + if !strings.HasSuffix(prefix, string(filepath.Separator)) { + prefix += string(filepath.Separator) + } + + return resolved == root || strings.HasPrefix(resolved, prefix), nil +} + +func (s *ScopedFs) Create(name string) (afero.File, error) { + if err := s.guard(name); err != nil { + return nil, err + } + return s.base.Create(name) +} + +func (s *ScopedFs) Mkdir(name string, perm os.FileMode) error { + if err := s.guard(name); err != nil { + return err + } + return s.base.Mkdir(name, perm) +} + +func (s *ScopedFs) MkdirAll(path string, perm os.FileMode) error { + if err := s.guard(path); err != nil { + return err + } + return s.base.MkdirAll(path, perm) +} + +func (s *ScopedFs) Open(name string) (afero.File, error) { + if err := s.guard(name); err != nil { + return nil, err + } + return s.base.Open(name) +} + +func (s *ScopedFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { + if err := s.guard(name); err != nil { + return nil, err + } + return s.base.OpenFile(name, flag, perm) +} + +func (s *ScopedFs) Remove(name string) error { + return s.base.Remove(name) +} + +func (s *ScopedFs) RemoveAll(path string) error { + return s.base.RemoveAll(path) +} + +func (s *ScopedFs) Rename(oldname, newname string) error { + if err := s.guard(oldname); err != nil { + return err + } + if err := s.guard(newname); err != nil { + return err + } + return s.base.Rename(oldname, newname) +} + +func (s *ScopedFs) Stat(name string) (os.FileInfo, error) { + if err := s.guard(name); err != nil { + return nil, err + } + return s.base.Stat(name) +} + +func (s *ScopedFs) Name() string { return "ScopedFs" } + +func (s *ScopedFs) Chmod(name string, mode os.FileMode) error { + if err := s.guard(name); err != nil { + return err + } + return s.base.Chmod(name, mode) +} + +func (s *ScopedFs) Chown(name string, uid, gid int) error { + if err := s.guard(name); err != nil { + return err + } + return s.base.Chown(name, uid, gid) +} + +func (s *ScopedFs) Chtimes(name string, atime, mtime time.Time) error { + if err := s.guard(name); err != nil { + return err + } + return s.base.Chtimes(name, atime, mtime) +} + +func (s *ScopedFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { + if err := s.guard(name); err != nil { + return nil, false, err + } + return s.base.LstatIfPossible(name) +} diff --git a/fileutils/copy_test.go b/fileutils/copy_test.go index f9cad6f61e..ccdcc9018b 100644 --- a/fileutils/copy_test.go +++ b/fileutils/copy_test.go @@ -5,6 +5,7 @@ import ( "path/filepath" "testing" + "github.com/filebrowser/filebrowser/v2/files" "github.com/spf13/afero" ) @@ -31,7 +32,7 @@ func TestCopyDoesNotDereferenceEscapingSymlink(t *testing.T) { t.Skipf("cannot create symlink: %v", err) } - afs := afero.NewBasePathFs(afero.NewOsFs(), scope) + afs := files.NewScopedFs(afero.NewOsFs(), scope) err := Copy(afs, "/srcdir", "/dstdir", 0o644, 0o755) if err == nil { @@ -58,7 +59,7 @@ func TestCopyAllowsInScopeSymlink(t *testing.T) { t.Skipf("cannot create symlink: %v", err) } - afs := afero.NewBasePathFs(afero.NewOsFs(), scope) + afs := files.NewScopedFs(afero.NewOsFs(), scope) if err := Copy(afs, "/srcdir", "/dstdir", 0o644, 0o755); err != nil { t.Fatalf("expected copy of an in-scope symlink to succeed, got: %v", err) diff --git a/fileutils/dir.go b/fileutils/dir.go index 0b127c482c..e0b049db40 100644 --- a/fileutils/dir.go +++ b/fileutils/dir.go @@ -3,24 +3,14 @@ package fileutils import ( "errors" "io/fs" - "os" "github.com/spf13/afero" - - "github.com/filebrowser/filebrowser/v2/files" ) // CopyDir copies a directory from source to dest and all // of its sub-directories. It doesn't stop if it finds an error // during the copy. Returns an error if any. func CopyDir(afs afero.Fs, source, dest string, fileMode, dirMode fs.FileMode) error { - if ok, err := files.WithinScope(afs, source); err != nil || !ok { - if err != nil { - return err - } - return os.ErrPermission - } - // Get properties of source. srcinfo, err := afs.Stat(source) if err != nil { diff --git a/fileutils/file.go b/fileutils/file.go index ab8c7fff3f..784f728f65 100644 --- a/fileutils/file.go +++ b/fileutils/file.go @@ -8,8 +8,6 @@ import ( "path/filepath" "github.com/spf13/afero" - - "github.com/filebrowser/filebrowser/v2/files" ) // MoveFile moves file from src to dst. @@ -34,13 +32,6 @@ func MoveFile(afs afero.Fs, src, dst string, fileMode, dirMode fs.FileMode) erro // CopyFile copies a file from source to dest and returns // an error if any. func CopyFile(afs afero.Fs, source, dest string, fileMode, dirMode fs.FileMode) error { - if ok, err := files.WithinScope(afs, source); err != nil || !ok { - if err != nil { - return err - } - return os.ErrPermission - } - // Open the source file. src, err := afs.Open(source) if err != nil { diff --git a/http/public.go b/http/public.go index fb71d2b8bf..a882f2f7af 100644 --- a/http/public.go +++ b/http/public.go @@ -9,11 +9,9 @@ import ( "path/filepath" "strings" - "github.com/spf13/afero" - "golang.org/x/crypto/bcrypt" - "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/share" + "golang.org/x/crypto/bcrypt" ) var withHashFile = func(fn handleFunc) handleFunc { @@ -65,8 +63,11 @@ var withHashFile = func(fn handleFunc) handleFunc { filePath = ifPath } - // set fs root to the shared file/folder - d.user.Fs = afero.NewBasePathFs(d.user.Fs, basePath) + // set fs root to the shared file/folder. ScopedFs (not a bare + // BasePathFs) so the share is also symlink-confined: a link inside the + // shared subtree that points elsewhere in the owner's scope — outside + // the share — must not be followed. + d.user.Fs = files.NewScopedFs(d.user.Fs, basePath) // the filesystem is now rebased onto basePath, so paths handed to the // rule checker are relative to it. Resolve them back to the user's diff --git a/http/public_symlink_test.go b/http/public_symlink_test.go index 89c3bbd35a..fa631c8238 100644 --- a/http/public_symlink_test.go +++ b/http/public_symlink_test.go @@ -12,13 +12,13 @@ import ( "testing" "github.com/asdine/storm/v3" - "github.com/spf13/afero" - + "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/share" "github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/storage/bolt" "github.com/filebrowser/filebrowser/v2/users" + "github.com/spf13/afero" ) // symlinkShareStorage builds a storage whose single user is rooted at a real @@ -65,7 +65,7 @@ func symlinkShareStorage(t *testing.T) *storage.Storage { } st.Users = &customFSUser{ Store: st.Users, - fs: afero.NewBasePathFs(afero.NewOsFs(), scope), + fs: files.NewScopedFs(afero.NewOsFs(), scope), } return st } diff --git a/http/public_test.go b/http/public_test.go index 947ee04947..0b0b76773a 100644 --- a/http/public_test.go +++ b/http/public_test.go @@ -8,13 +8,13 @@ import ( "testing" "github.com/asdine/storm/v3" - "github.com/spf13/afero" - + "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/share" "github.com/filebrowser/filebrowser/v2/storage/bolt" "github.com/filebrowser/filebrowser/v2/users" + "github.com/spf13/afero" ) func TestPublicShareHandlerAuthentication(t *testing.T) { @@ -220,7 +220,7 @@ func TestPublicShareHandlerRules(t *testing.T) { t.Fatalf("failed to save settings: %v", err) } - fs := afero.NewBasePathFs(afero.NewOsFs(), t.TempDir()) + fs := files.NewScopedFs(afero.NewOsFs(), t.TempDir()) if err := fs.MkdirAll("/projects/private", 0o755); err != nil { t.Fatalf("failed to create private dir: %v", err) } @@ -276,7 +276,9 @@ func (cu *customFSUser) Get(baseScope string, id interface{}) (*users.User, erro if err != nil { return nil, err } - user.Fs = cu.fs + // Mirror production (users.User init), where a user's filesystem is always a + // scoped, symlink-confining ScopedFs rather than a bare afero.Fs. + user.Fs = files.NewScopedFs(cu.fs, "/") return user, nil } diff --git a/http/raw.go b/http/raw.go index ab01eed6ad..860d6aa71f 100644 --- a/http/raw.go +++ b/http/raw.go @@ -10,11 +10,10 @@ import ( "path/filepath" "strings" - "github.com/mholt/archives" - "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/fileutils" "github.com/filebrowser/filebrowser/v2/users" + "github.com/mholt/archives" ) func slashClean(name string) string { @@ -115,10 +114,6 @@ func getFiles(d *data, path, commonPath string) ([]archives.FileInfo, error) { return nil, nil } - if ok, err := files.WithinScope(d.user.Fs, path); err != nil || !ok { - return nil, nil - } - info, err := d.user.Fs.Stat(path) if err != nil { return nil, err diff --git a/http/resource.go b/http/resource.go index 27eb8b70f2..728bad738a 100644 --- a/http/resource.go +++ b/http/resource.go @@ -15,12 +15,11 @@ import ( "strings" "time" - "github.com/shirou/gopsutil/v4/disk" - "github.com/spf13/afero" - fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/fileutils" + "github.com/shirou/gopsutil/v4/disk" + "github.com/spf13/afero" ) var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { @@ -228,15 +227,6 @@ func resourcePatchHandler(fileCache FileCache) handleFunc { return http.StatusForbidden, nil } - for _, p := range []string{src, dst} { - if ok, scopeErr := files.WithinScope(d.user.Fs, p); scopeErr != nil || !ok { - if scopeErr != nil { - return errToStatus(scopeErr), scopeErr - } - return http.StatusForbidden, nil - } - } - err = checkParent(src, dst) if err != nil { return http.StatusBadRequest, err @@ -304,10 +294,6 @@ func addVersionSuffix(source string, afs afero.Fs) string { } func writeFile(afs afero.Fs, dst string, in io.Reader, fileMode, dirMode fs.FileMode) (os.FileInfo, error) { - if ok, err := files.WithinScope(afs, dst); err != nil || !ok { - return nil, os.ErrPermission - } - dir, _ := path.Split(dst) err := afs.MkdirAll(dir, dirMode) if err != nil { diff --git a/http/share.go b/http/share.go index c73f5daf58..7cd08fafd5 100644 --- a/http/share.go +++ b/http/share.go @@ -12,11 +12,9 @@ import ( "strings" "time" - "golang.org/x/crypto/bcrypt" - fberrors "github.com/filebrowser/filebrowser/v2/errors" - "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/share" + "golang.org/x/crypto/bcrypt" ) func withPermShare(fn handleFunc) handleFunc { @@ -103,20 +101,14 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request // Only allow sharing paths that currently exist. Otherwise a share could be // created for a non-existent path and would silently start exposing // whatever file later appears there. + // + // d.user.Fs is scoped, so Stat also refuses to follow a symlink whose target + // escapes the user's scope: that returns a permission error here and so + // blocks creating a share that points out of scope. if _, err := d.user.Fs.Stat(r.URL.Path); err != nil { return errToStatus(err), err } - // Refuse to create a share whose on-disk target escapes the user's scope - // (e.g. via a symlink), mirroring the read/write guards. The public serve - // path already rejects these, but blocking creation avoids dangling shares. - if ok, err := files.WithinScope(d.user.Fs, r.URL.Path); err != nil || !ok { - if err != nil { - return errToStatus(err), err - } - return http.StatusForbidden, nil - } - var s *share.Link var body share.CreateBody if r.Body != nil { diff --git a/http/tus_handlers.go b/http/tus_handlers.go index 2629bd09f0..fe7ae2804e 100644 --- a/http/tus_handlers.go +++ b/http/tus_handlers.go @@ -11,9 +11,8 @@ import ( "strings" "time" - "github.com/spf13/afero" - "github.com/filebrowser/filebrowser/v2/files" + "github.com/spf13/afero" ) // keepUploadActive periodically touches the cache entry to prevent eviction during transfer @@ -45,10 +44,6 @@ func tusPostHandler(cache UploadCache) handleFunc { return http.StatusForbidden, nil } - if ok, scopeErr := files.WithinScope(d.user.Fs, r.URL.Path); scopeErr != nil || !ok { - return http.StatusForbidden, nil - } - file, err := files.NewFileInfo(&files.FileOptions{ Fs: d.user.Fs, Path: r.URL.Path, @@ -167,10 +162,6 @@ func tusPatchHandler(cache UploadCache) handleFunc { return http.StatusUnsupportedMediaType, nil } - if ok, scopeErr := files.WithinScope(d.user.Fs, r.URL.Path); scopeErr != nil || !ok { - return http.StatusForbidden, nil - } - uploadOffset, err := getUploadOffset(r) if err != nil { return http.StatusBadRequest, fmt.Errorf("invalid upload offset") diff --git a/http/tus_symlink_test.go b/http/tus_symlink_test.go index ac0b8c3db0..678347e956 100644 --- a/http/tus_symlink_test.go +++ b/http/tus_symlink_test.go @@ -12,6 +12,7 @@ import ( "github.com/golang-jwt/jwt/v5" "github.com/spf13/afero" + "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/storage/bolt" "github.com/filebrowser/filebrowser/v2/users" @@ -58,7 +59,7 @@ func TestTusHandlersRejectSymlinkScopeEscape(t *testing.T) { } st.Users = &customFSUser{ Store: st.Users, - fs: afero.NewBasePathFs(afero.NewOsFs(), userScope), + fs: files.NewScopedFs(afero.NewOsFs(), userScope), } // Forge a valid auth token for user ID 1. diff --git a/users/users.go b/users/users.go index 7181b299e6..6be3b09d1b 100644 --- a/users/users.go +++ b/users/users.go @@ -3,11 +3,10 @@ package users import ( "path/filepath" - "github.com/spf13/afero" - fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/rules" + "github.com/spf13/afero" ) // ViewMode describes a view mode. @@ -20,23 +19,23 @@ const ( // User describes a user. type User struct { - ID uint `storm:"id,increment" json:"id"` - Username string `storm:"unique" json:"username"` - Password string `json:"password"` - Scope string `json:"scope"` - Locale string `json:"locale"` - LockPassword bool `json:"lockPassword"` - ViewMode ViewMode `json:"viewMode"` - SingleClick bool `json:"singleClick"` - RedirectAfterCopyMove bool `json:"redirectAfterCopyMove"` - Perm Permissions `json:"perm"` - Commands []string `json:"commands"` - Sorting files.Sorting `json:"sorting"` - Fs afero.Fs `json:"-" yaml:"-"` - Rules []rules.Rule `json:"rules"` - HideDotfiles bool `json:"hideDotfiles"` - DateFormat bool `json:"dateFormat"` - AceEditorTheme string `json:"aceEditorTheme"` + ID uint `storm:"id,increment" json:"id"` + Username string `storm:"unique" json:"username"` + Password string `json:"password"` + Scope string `json:"scope"` + Locale string `json:"locale"` + LockPassword bool `json:"lockPassword"` + ViewMode ViewMode `json:"viewMode"` + SingleClick bool `json:"singleClick"` + RedirectAfterCopyMove bool `json:"redirectAfterCopyMove"` + Perm Permissions `json:"perm"` + Commands []string `json:"commands"` + Sorting files.Sorting `json:"sorting"` + Fs *files.ScopedFs `json:"-" yaml:"-"` + Rules []rules.Rule `json:"rules"` + HideDotfiles bool `json:"hideDotfiles"` + DateFormat bool `json:"dateFormat"` + AceEditorTheme string `json:"aceEditorTheme"` } // GetRules implements rules.Provider. @@ -93,7 +92,7 @@ func (u *User) Clean(baseScope string, fields ...string) error { if u.Fs == nil { scope := u.Scope scope = filepath.Join(baseScope, filepath.Join("/", scope)) - u.Fs = afero.NewBasePathFs(afero.NewOsFs(), scope) + u.Fs = files.NewScopedFs(afero.NewOsFs(), scope) } return nil @@ -101,5 +100,5 @@ func (u *User) Clean(baseScope string, fields ...string) error { // FullPath gets the full path for a user's relative path. func (u *User) FullPath(path string) string { - return afero.FullBaseFsPath(u.Fs.(*afero.BasePathFs), path) + return afero.FullBaseFsPath(u.Fs.Base(), path) } From d9816b153194198d717e14cdbe32f83ef57e076c Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 7 Jun 2026 18:21:13 +0200 Subject: [PATCH 42/53] chore: add symlink tests --- http/resource_test.go | 117 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 http/resource_test.go diff --git a/http/resource_test.go b/http/resource_test.go new file mode 100644 index 0000000000..7081206f3b --- /dev/null +++ b/http/resource_test.go @@ -0,0 +1,117 @@ +package fbhttp + +import ( + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + "time" + + "github.com/asdine/storm/v3" + "github.com/golang-jwt/jwt/v5" + "github.com/spf13/afero" + + "github.com/filebrowser/filebrowser/v2/diskcache" + "github.com/filebrowser/filebrowser/v2/settings" + "github.com/filebrowser/filebrowser/v2/storage/bolt" + "github.com/filebrowser/filebrowser/v2/users" +) + +func TestResourceCopyDoesNotDereferenceEscapingSymlink(t *testing.T) { + root := t.TempDir() + userScope := filepath.Join(root, "user") + if err := os.MkdirAll(filepath.Join(userScope, "srcdir"), 0o755); err != nil { + t.Fatal(err) + } + + // An ordinary in-scope file, to prove a normal copy still works. + if err := os.WriteFile(filepath.Join(userScope, "srcdir", "normal.txt"), []byte("in-scope"), 0o644); err != nil { + t.Fatal(err) + } + + // The secret living outside the user's scope. + secret := filepath.Join(root, "secret.txt") + if err := os.WriteFile(secret, []byte("OUT-OF-SCOPE-SECRET"), 0o644); err != nil { + t.Fatal(err) + } + + // An escaping symlink planted inside the user's scope (out-of-band, as the + // advisory's preconditions describe). + if err := os.Symlink(secret, filepath.Join(userScope, "srcdir", "link.txt")); err != nil { + t.Skipf("cannot create symlink: %v", err) + } + + key := []byte("test-signing-key") + + db, err := storm.Open(filepath.Join(t.TempDir(), "db")) + if err != nil { + t.Fatalf("failed to open db: %v", err) + } + t.Cleanup(func() { _ = db.Close() }) + + st, err := bolt.NewStorage(db) + if err != nil { + t.Fatalf("failed to get storage: %v", err) + } + perm := users.Permissions{Create: true, Modify: true, Download: true, Rename: true} + if err := st.Users.Save(&users.User{Username: "u", Password: "pw", Perm: perm}); err != nil { + t.Fatalf("failed to save user: %v", err) + } + if err := st.Settings.Save(&settings.Settings{Key: key}); err != nil { + t.Fatalf("failed to save settings: %v", err) + } + // The user's scope is the real on-disk userScope. customFSUser.Get wraps it + // in a ScopedFs, mirroring production (users.User init). + st.Users = &customFSUser{ + Store: st.Users, + fs: afero.NewBasePathFs(afero.NewOsFs(), userScope), + } + + signed := signToken(t, perm, key) + + t.Run("direct raw read is forbidden", func(t *testing.T) { + req, _ := http.NewRequest(http.MethodGet, "/srcdir/link.txt", http.NoBody) + req.Header.Set("X-Auth", signed) + rec := httptest.NewRecorder() + handle(rawHandler, "", st, &settings.Server{}).ServeHTTP(rec, req) + if rec.Code == http.StatusOK { + t.Fatalf("VULNERABLE: direct raw read of escaping symlink returned 200, body=%q", rec.Body.String()) + } + }) + + t.Run("recursive copy does not exfiltrate", func(t *testing.T) { + req, _ := http.NewRequest(http.MethodPatch, "/srcdir/", http.NoBody) + q := req.URL.Query() + q.Set("action", "copy") + q.Set("destination", "/dstdir") + req.URL.RawQuery = q.Encode() + req.Header.Set("X-Auth", signed) + + rec := httptest.NewRecorder() + handle(resourcePatchHandler(diskcache.NewNoOp()), "", st, &settings.Server{}).ServeHTTP(rec, req) + t.Logf("copy status=%d body=%q", rec.Code, rec.Body.String()) + + // The escaping symlink's target content must never appear in scope. + leaked := filepath.Join(userScope, "dstdir", "link.txt") + if data, readErr := os.ReadFile(leaked); readErr == nil { + t.Fatalf("VULNERABLE: out-of-scope content landed in scope at %s: %q", leaked, string(data)) + } + }) +} + +func signToken(t *testing.T, perm users.Permissions, key []byte) string { + t.Helper() + claims := &authToken{ + User: userInfo{ID: 1, Username: "u", Perm: perm}, + RegisteredClaims: jwt.RegisteredClaims{ + IssuedAt: jwt.NewNumericDate(time.Now().Add(-time.Minute)), + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), + }, + } + signed, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(key) + if err != nil { + t.Fatalf("failed to sign token: %v", err) + } + return signed +} From dfe6e5b333e3211dd6ced146672657de598299c7 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 7 Jun 2026 18:21:40 +0200 Subject: [PATCH 43/53] chore(release): 2.63.14 --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3129149f2b..f545a0386b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.63.14](https://github.com/filebrowser/filebrowser/compare/v2.63.13...v2.63.14) (2026-06-07) + + +### Bug Fixes + +* recursive check ([3406d3d](https://github.com/filebrowser/filebrowser/commit/3406d3d7f98dfc3c16e4ff7ff4a87e3bdfe221dd)) + + +### Refactorings + +* ScopedFs to avoid escaping symlinks ([7c2c0a1](https://github.com/filebrowser/filebrowser/commit/7c2c0a11b31b2bb214d741005a0b02b1764208b3)) + ## [2.63.13](https://github.com/filebrowser/filebrowser/compare/v2.63.12...v2.63.13) (2026-06-06) From 403d2bbd3357aa57e78745b52dcdad3490901bb3 Mon Sep 17 00:00:00 2001 From: yfzhou Date: Sat, 13 Jun 2026 13:17:48 +0800 Subject: [PATCH 44/53] fix: restore ScopedFs RealPath (#5986) --- files/file_test.go | 14 ++++++++++++++ files/scoped.go | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/files/file_test.go b/files/file_test.go index 1efda7f601..b5a8b37fa0 100644 --- a/files/file_test.go +++ b/files/file_test.go @@ -212,3 +212,17 @@ func TestReadListingSkipsInaccessibleChildren(t *testing.T) { t.Fatalf("expected one listed directory, got %d", got) } } + +func TestFileInfoRealPathUsesScopedFsRealPath(t *testing.T) { + root := t.TempDir() + file := &FileInfo{ + Fs: NewScopedFs(afero.NewOsFs(), root), + Path: "/root/downloads", + } + + got := file.RealPath() + want := filepath.Join(root, "root", "downloads") + if got != want { + t.Fatalf("got %q, want %q", got, want) + } +} diff --git a/files/scoped.go b/files/scoped.go index e6e1843512..eef6378b98 100644 --- a/files/scoped.go +++ b/files/scoped.go @@ -35,6 +35,13 @@ func NewScopedFs(source afero.Fs, path string) *ScopedFs { // Base returns the underlying *afero.BasePathFs. func (s *ScopedFs) Base() *afero.BasePathFs { return s.base } +// RealPath resolves a scoped path to the real on-disk path by delegating to +// the underlying BasePathFs. This is needed by callers that need the actual +// filesystem path (e.g. disk.UsageWithContext). +func (s *ScopedFs) RealPath(name string) (string, error) { + return s.base.RealPath(name) +} + // guard returns an error if name's on-disk target resolves outside the scope. func (s *ScopedFs) guard(name string) error { ok, err := s.within(name) From ffb486e05fe0d38f09a3067d1f16a71628726d15 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 13 Jun 2026 07:19:01 +0200 Subject: [PATCH 45/53] chore: bump minor Go dependencies --- go.mod | 50 +++++----- go.sum | 311 ++++++++++----------------------------------------------- 2 files changed, 76 insertions(+), 285 deletions(-) diff --git a/go.mod b/go.mod index 4442b5648b..b333522eec 100644 --- a/go.mod +++ b/go.mod @@ -16,9 +16,9 @@ require ( github.com/marusama/semaphore/v2 v2.5.0 github.com/mholt/archives v0.1.5 github.com/mitchellh/go-homedir v1.1.0 - github.com/redis/go-redis/v9 v9.19.0 + github.com/redis/go-redis/v9 v9.20.1 github.com/samber/lo v1.53.0 - github.com/shirou/gopsutil/v4 v4.26.4 + github.com/shirou/gopsutil/v4 v4.26.5 github.com/spf13/afero v1.15.0 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 @@ -26,59 +26,59 @@ require ( github.com/stretchr/testify v1.11.1 github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce go.etcd.io/bbolt v1.4.3 - golang.org/x/crypto v0.50.0 - golang.org/x/image v0.39.0 - golang.org/x/text v0.36.0 + golang.org/x/crypto v0.53.0 + golang.org/x/image v0.42.0 + golang.org/x/text v0.38.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/STARRY-S/zip v0.2.3 // indirect - github.com/andybalholm/brotli v1.2.0 // indirect - github.com/asticode/go-astikit v0.56.0 // indirect - github.com/asticode/go-astits v1.13.0 // indirect + github.com/andybalholm/brotli v1.2.1 // indirect + github.com/asticode/go-astikit v0.59.0 // indirect + github.com/asticode/go-astits v1.15.0 // indirect github.com/bodgit/plumbing v1.3.0 // indirect - github.com/bodgit/sevenzip v1.6.1 // indirect + github.com/bodgit/sevenzip v1.6.4 // indirect github.com/bodgit/windows v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect - github.com/ebitengine/purego v0.10.0 // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/ebitengine/purego v0.10.1 // indirect + github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/golang/geo v0.0.0-20250707181242-c5087ca84cf4 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/golang/geo v0.0.0-20260612074446-f1a45663b0f3 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/compress v1.18.6 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/mikelolasagasti/xz v1.0.1 // indirect - github.com/minio/minlz v1.0.1 // indirect - github.com/nwaples/rardecode/v2 v2.2.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/pierrec/lz4/v4 v4.1.22 // indirect + github.com/minio/minlz v1.1.1 // indirect + github.com/nwaples/rardecode/v2 v2.2.3 // indirect + github.com/pelletier/go-toml/v2 v2.3.1 // indirect + github.com/pierrec/lz4/v4 v4.1.27 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/sorairolake/lzip-go v0.3.8 // indirect - github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/cast v1.10.0 // indirect + github.com/stangelandcl/ppmd v0.1.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/ulikunitz/xz v0.5.15 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/net v0.52.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.43.0 // indirect + go4.org v0.0.0-20260112195520-a5071408f32f // indirect + golang.org/x/net v0.56.0 // indirect + golang.org/x/sync v0.21.0 // indirect + golang.org/x/sys v0.46.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index d756184d1e..c228b34569 100644 --- a/go.sum +++ b/go.sum @@ -1,60 +1,37 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4= github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk= github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863 h1:BRrxwOZBolJN4gIwvZMJY1tzqBvQgpaZiQRuIDD40jM= github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM= -github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= -github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro= +github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/asdine/storm/v3 v3.2.1 h1:I5AqhkPK6nBZ/qJXySdI7ot5BlXSZ7qvDY1zAn5ZJac= github.com/asdine/storm/v3 v3.2.1/go.mod h1:LEpXwGt4pIqrE/XcTvCnZHT5MgZCV6Ub9q7yQzOFWr0= github.com/asticode/go-astikit v0.20.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0= github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0= -github.com/asticode/go-astikit v0.56.0 h1:DmD2p7YnvxiPdF0h+dRmos3bsejNEXbycENsY5JfBqw= -github.com/asticode/go-astikit v0.56.0/go.mod h1:fV43j20UZYfXzP9oBn33udkvCvDvCDhzjVqoLFuuYZE= +github.com/asticode/go-astikit v0.59.0 h1:tjbwDym+MTSxqkAhJoHRZmHMXK6Jv4vGx+97FptKH6k= +github.com/asticode/go-astikit v0.59.0/go.mod h1:fV43j20UZYfXzP9oBn33udkvCvDvCDhzjVqoLFuuYZE= github.com/asticode/go-astisub v0.40.0 h1:Z8tAXHngjkh/4L9zqt49ru9UdpDTqBIe+80xexKM3/I= github.com/asticode/go-astisub v0.40.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= github.com/asticode/go-astits v1.8.0/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ= -github.com/asticode/go-astits v1.13.0 h1:XOgkaadfZODnyZRR5Y0/DWkA9vrkLLPLeeOvDwfKZ1c= -github.com/asticode/go-astits v1.13.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI= +github.com/asticode/go-astits v1.15.0 h1:yRyCiUc8Jj4F7clt2GDxHghMpWuFL5rkaLuGUd2/0J4= +github.com/asticode/go-astits v1.15.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI= github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= -github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4= -github.com/bodgit/sevenzip v1.6.1/go.mod h1:GVoYQbEVbOGT8n2pfqCIMRUaRjQ8F9oSqoBEqZh5fQ8= +github.com/bodgit/sevenzip v1.6.4 h1:iHiVJfxbrB6RF4X+snI2MpVgNBKmVfGaTqZGNlMQIU0= +github.com/bodgit/sevenzip v1.6.4/go.mod h1:ZtNi5KNgHXeXg1G7WiF0IWSuFE2eG6lt/cTGlvuirO0= github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -81,89 +58,56 @@ github.com/dsoprea/go-utility/v2 v2.0.0-20221003142440-7a1927d49d9d/go.mod h1:LV github.com/dsoprea/go-utility/v2 v2.0.0-20221003160719-7bc88537c05e/go.mod h1:VZ7cB0pTjm1ADBWhJUOHESu4ZYy9JN+ZPqjfiW09EPU= github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 h1:DilThiXje0z+3UQ5YjYiSRRzVdtamFpvBQXKwMglWqw= github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349/go.mod h1:4GC5sXji84i/p+irqghpPFZBF8tRN/Q7+700G0/DLe8= -github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= -github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY= +github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= +github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= -github.com/golang/geo v0.0.0-20250707181242-c5087ca84cf4 h1:vCeHcs8N7MOccOOsOVIy1xcYu+kBkA4J5urTgigww7c= -github.com/golang/geo v0.0.0-20250707181242-c5087ca84cf4/go.mod h1:AN0OjM34c3PbjAsX+QNma1nYtJtRxl+s9MZNV7S+efw= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/geo v0.0.0-20260612074446-f1a45663b0f3 h1:UlucSQUu9SZdDRlMWv5N/T3Rig9gv615vWvHFKrXHWA= +github.com/golang/geo v0.0.0-20260612074446-f1a45663b0f3/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY= github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= @@ -186,41 +130,36 @@ github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ= github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4= github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0= github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc= -github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A= -github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec= +github.com/minio/minlz v1.1.1 h1:OGmft1V6AnI/Wme332U6bhG54nxEan+VFgkD7lat4KM= +github.com/minio/minlz v1.1.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/nwaples/rardecode/v2 v2.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A= -github.com/nwaples/rardecode/v2 v2.2.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= -github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/nwaples/rardecode/v2 v2.2.3 h1:qaVuy3ChZDbAQZshPLjHeNJKF3Cru8uo9jmgveKIy2A= +github.com/nwaples/rardecode/v2 v2.2.3/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= +github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= +github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk= +github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k= -github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/redis/go-redis/v9 v9.20.1 h1:sfCU6A8P3dXbKyWes02uxA2baehGux9dZHfEKtsTB1w= +github.com/redis/go-redis/v9 v9.20.1/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= -github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= +github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM= github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= -github.com/shirou/gopsutil/v4 v4.26.4 h1:B4SXVbcwTyrocPHEmWBC4uCYr4Xcu3MK1TXqbprAOWY= -github.com/shirou/gopsutil/v4 v4.26.4/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/shirou/gopsutil/v4 v4.26.5 h1:RPcBXkpz7kOj9PqGFQOlBPZHsyaPvPVQc098y9RmCNM= +github.com/shirou/gopsutil/v4 v4.26.5/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik= github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= @@ -232,6 +171,8 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/stangelandcl/ppmd v0.1.1 h1:c25QazhlWUn5nmR1QOzafKhQxBicAr7GGCKER2aJ8H8= +github.com/stangelandcl/ppmd v0.1.1/go.mod h1:Rrv7M+/2P5jYr/GMLhBl7Ug3uJ1bUiVzr5LbbaV6xgY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -255,7 +196,6 @@ github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaU github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= @@ -263,202 +203,61 @@ github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= -go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= +go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH9QQjfb0Sw= +go4.org v0.0.0-20260112195520-a5071408f32f/go.mod h1:ZRJnO5ZI4zAwMFp+dS1+V6J6MSyAowhRqAE+DPa1Xp0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= +golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww= -golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/image v0.42.0 h1:1gSs6ehNWXLbkHBIPcWztk3D/6aIA/8hauiAYtlodVY= +golang.org/x/image v0.42.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= -golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -469,11 +268,3 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From be23ab3a15bf957928ecfed88de5ab67850c1b9c Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 13 Jun 2026 07:20:45 +0200 Subject: [PATCH 46/53] chore(release): 2.63.15 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f545a0386b..6d5e61a564 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.63.15](https://github.com/filebrowser/filebrowser/compare/v2.63.14...v2.63.15) (2026-06-13) + + +### Bug Fixes + +* restore ScopedFs RealPath ([#5986](https://github.com/filebrowser/filebrowser/issues/5986)) ([403d2bb](https://github.com/filebrowser/filebrowser/commit/403d2bbd3357aa57e78745b52dcdad3490901bb3)) + ## [2.63.14](https://github.com/filebrowser/filebrowser/compare/v2.63.13...v2.63.14) (2026-06-07) From daa2adf3045960c93b8dff29b1ff15d7941888be Mon Sep 17 00:00:00 2001 From: Michal Pryc Date: Sat, 18 Oct 2025 21:32:21 +0200 Subject: [PATCH 47/53] UPSTREAM: Add disableUserProfile branding option New config option: --branding.disableUserProfile This option allows to disable User Profile together with User settings. Signed-off-by: Michal Pryc --- cmd/config.go | 9 +- cmd/config_init.go | 129 ++++++++++++++++++++++++- cmd/config_set.go | 56 ++++++++++- frontend/index.html | 1 + frontend/src/components/Sidebar.vue | 6 +- frontend/src/i18n/en.json | 1 + frontend/src/router/index.ts | 18 +++- frontend/src/types/settings.d.ts | 1 + frontend/src/utils/constants.ts | 2 + frontend/src/views/Settings.vue | 3 +- frontend/src/views/settings/Global.vue | 9 ++ http/static.go | 1 + settings/branding.go | 1 + 13 files changed, 228 insertions(+), 9 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index e3bb2b862d..140a3f0e51 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -57,9 +57,11 @@ func addConfigFlags(flags *pflag.FlagSet) { flags.String("branding.files", "", "path to directory with images and custom styles") flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links") flags.Bool("branding.disableUsedPercentage", false, "disable used disk percentage graph") - - flags.Uint64("tus.chunkSize", settings.DefaultTusChunkSize, "the tus chunk size") - flags.Uint16("tus.retryCount", settings.DefaultTusRetryCount, "the tus retry count") + flags.Bool("branding.disableUserProfile", false, "disable user profile in sidebar and settings/profile page") + // NB: these are string so they can be presented as octal in the help text + // as that's the conventional representation for modes in Unix. + flags.String("file-mode", fmt.Sprintf("%O", settings.DefaultFileMode), "Mode bits that new files are created with") + flags.String("dir-mode", fmt.Sprintf("%O", settings.DefaultDirMode), "Mode bits that new directories are created with") } func getAuthMethod(flags *pflag.FlagSet, defaults ...interface{}) (settings.AuthMethod, map[string]interface{}, error) { @@ -212,6 +214,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Branding.Files) fmt.Fprintf(w, "\tDisable external links:\t%t\n", set.Branding.DisableExternal) fmt.Fprintf(w, "\tDisable used disk percentage graph:\t%t\n", set.Branding.DisableUsedPercentage) + fmt.Fprintf(w, "\tDisable user profile:\t%t\n", set.Branding.DisableUserProfile) fmt.Fprintf(w, "\tColor:\t%s\n", set.Branding.Color) fmt.Fprintf(w, "\tTheme:\t%s\n", set.Branding.Theme) diff --git a/cmd/config_init.go b/cmd/config_init.go index 359d02a343..eb1549bd51 100644 --- a/cmd/config_init.go +++ b/cmd/config_init.go @@ -46,7 +46,134 @@ override the options.`, return err } - err = st.Auth.Save(auther) + minLength, err := getUint(flags, "minimum-password-length") + if err != nil { + return err + } + + shell, err := getString(flags, "shell") + if err != nil { + return err + } + + brandingName, err := getString(flags, "branding.name") + if err != nil { + return err + } + + brandingDisableExternal, err := getBool(flags, "branding.disableExternal") + if err != nil { + return err + } + + brandingDisableUsedPercentage, err := getBool(flags, "branding.disableUsedPercentage") + if err != nil { + return err + } + + brandingDisableUserProfile, err := getBool(flags, "branding.disableUserProfile") + if err != nil { + return err + } + + brandingTheme, err := getString(flags, "branding.theme") + if err != nil { + return err + } + + brandingFiles, err := getString(flags, "branding.files") + if err != nil { + return err + } + + s := &settings.Settings{ + Key: key, + Signup: signup, + CreateUserDir: createUserDir, + MinimumPasswordLength: minLength, + Shell: convertCmdStrToCmdArray(shell), + AuthMethod: authMethod, + Defaults: defaults, + Branding: settings.Branding{ + Name: brandingName, + DisableExternal: brandingDisableExternal, + DisableUsedPercentage: brandingDisableUsedPercentage, + DisableUserProfile: brandingDisableUserProfile, + Theme: brandingTheme, + Files: brandingFiles, + }, + } + + s.FileMode, err = getMode(flags, "file-mode") + if err != nil { + return err + } + + s.DirMode, err = getMode(flags, "dir-mode") + if err != nil { + return err + } + + address, err := getString(flags, "address") + if err != nil { + return err + } + + socket, err := getString(flags, "socket") + if err != nil { + return err + } + + root, err := getString(flags, "root") + if err != nil { + return err + } + + baseURL, err := getString(flags, "baseurl") + if err != nil { + return err + } + + tlsKey, err := getString(flags, "key") + if err != nil { + return err + } + + cert, err := getString(flags, "cert") + if err != nil { + return err + } + + port, err := getString(flags, "port") + if err != nil { + return err + } + + log, err := getString(flags, "log") + if err != nil { + return err + } + + ser := &settings.Server{ + Address: address, + Socket: socket, + Root: root, + BaseURL: baseURL, + TLSKey: tlsKey, + TLSCert: cert, + Port: port, + Log: log, + } + + err = d.store.Settings.Save(s) + if err != nil { + return err + } + err = d.store.Settings.SaveServer(ser) + if err != nil { + return err + } + err = d.store.Auth.Save(auther) if err != nil { return err } diff --git a/cmd/config_set.go b/cmd/config_set.go index df357a0265..626e6e82f0 100644 --- a/cmd/config_set.go +++ b/cmd/config_set.go @@ -29,7 +29,61 @@ you want to change. Other options will remain unchanged.`, return err } - auther, err := st.Auth.Get(set.AuthMethod) + hasAuth := false + flags.Visit(func(flag *pflag.Flag) { + if err != nil { + return + } + switch flag.Name { + case "baseurl": + ser.BaseURL, err = getString(flags, flag.Name) + case "root": + ser.Root, err = getString(flags, flag.Name) + case "socket": + ser.Socket, err = getString(flags, flag.Name) + case "cert": + ser.TLSCert, err = getString(flags, flag.Name) + case "key": + ser.TLSKey, err = getString(flags, flag.Name) + case "address": + ser.Address, err = getString(flags, flag.Name) + case "port": + ser.Port, err = getString(flags, flag.Name) + case "log": + ser.Log, err = getString(flags, flag.Name) + case "signup": + set.Signup, err = getBool(flags, flag.Name) + case "auth.method": + hasAuth = true + case "shell": + var shell string + shell, err = getString(flags, flag.Name) + set.Shell = convertCmdStrToCmdArray(shell) + case "create-user-dir": + set.CreateUserDir, err = getBool(flags, flag.Name) + case "minimum-password-length": + set.MinimumPasswordLength, err = getUint(flags, flag.Name) + case "branding.name": + set.Branding.Name, err = getString(flags, flag.Name) + case "branding.color": + set.Branding.Color, err = getString(flags, flag.Name) + case "branding.theme": + set.Branding.Theme, err = getString(flags, flag.Name) + case "branding.disableExternal": + set.Branding.DisableExternal, err = getBool(flags, flag.Name) + case "branding.disableUsedPercentage": + set.Branding.DisableUsedPercentage, err = getBool(flags, flag.Name) + case "branding.disableUserProfile": + set.Branding.DisableUserProfile, err = getBool(flags, flag.Name) + case "branding.files": + set.Branding.Files, err = getString(flags, flag.Name) + case "file-mode": + set.FileMode, err = getMode(flags, flag.Name) + case "dir-mode": + set.DirMode, err = getMode(flags, flag.Name) + } + }) + if err != nil { return err } diff --git a/frontend/index.html b/frontend/index.html index 19308a9543..ffa4df8545 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -37,6 +37,7 @@ Color: "", DisableExternal: false, DisableUsedPercentage: false, + DisableUserProfile: false, EnableExec: true, EnableThumbs: true, LogoutPage: "", diff --git a/frontend/src/components/Sidebar.vue b/frontend/src/components/Sidebar.vue index ff74aa40eb..6578a928b6 100644 --- a/frontend/src/components/Sidebar.vue +++ b/frontend/src/components/Sidebar.vue @@ -2,7 +2,7 @@