Compare commits

..

1 Commits

Author SHA1 Message Date
renovate[bot]
5e382ed73e Update dependency @rolldown/plugin-babel to v0.3.0 2026-11-01 15:13:03 +00:00
20 changed files with 166 additions and 569 deletions

View File

@@ -163,13 +163,13 @@
} }
}, },
"node_modules/@babel/generator": { "node_modules/@babel/generator": {
"version": "7.29.12", "version": "7.29.8",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.12.tgz", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz",
"integrity": "sha512-2W76mhjrpQFO1lK5e8IvGYB2D0YZ7S2R1Z2BjO2ZUYuW+amcpK/z60U6ZZmzskwoa7sJ6XohP/Wg8H5KxXI/IA==", "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.29.12", "@babel/parser": "^7.29.8",
"@babel/types": "^7.29.11", "@babel/types": "^7.29.8",
"@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28", "@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2" "jsesc": "^3.0.2"
@@ -283,9 +283,9 @@
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.29.12", "version": "7.29.11",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.12.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.11.tgz",
"integrity": "sha512-WJ7JBp/Q4+z/ImydtI7jS6NVco5++632ExBemSX+Ysb+G1lCwQ5BVo3SY+jGGOcZXI4ZccbZSUSJLNBW6eEpJg==", "integrity": "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/types": "^7.29.11" "@babel/types": "^7.29.11"
@@ -321,17 +321,17 @@
} }
}, },
"node_modules/@babel/traverse": { "node_modules/@babel/traverse": {
"version": "7.29.12", "version": "7.29.10",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.12.tgz", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.10.tgz",
"integrity": "sha512-MscXe/NAZcOkYEsRajHZFt3ynsb22PUi6CTrp5U7+RYycR9By84mn9mFN54YgfITiqK4hFx3Ju3tZSyzfpS5ew==", "integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.29.7", "@babel/code-frame": "^7.29.7",
"@babel/generator": "^7.29.12", "@babel/generator": "^7.29.8",
"@babel/helper-globals": "^7.29.7", "@babel/helper-globals": "^7.29.7",
"@babel/parser": "^7.29.12", "@babel/parser": "^7.29.9",
"@babel/template": "^7.29.7", "@babel/template": "^7.29.7",
"@babel/types": "^7.29.11", "@babel/types": "^7.29.8",
"debug": "^4.3.1" "debug": "^4.3.1"
}, },
"engines": { "engines": {
@@ -631,9 +631,9 @@
} }
}, },
"node_modules/@csstools/css-color-parser": { "node_modules/@csstools/css-color-parser": {
"version": "4.2.7", "version": "4.2.6",
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.7.tgz", "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.6.tgz",
"integrity": "sha512-zgm9yjQz5iJB4VL8RyVwd71TWB8Q89FSxt/NzXoWqPSkEBOVtn9L6vAPtSc9hpKfB0Awo/AL2VKFHjYetBLFHQ==", "integrity": "sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -682,9 +682,9 @@
} }
}, },
"node_modules/@csstools/css-syntax-patches-for-csstree": { "node_modules/@csstools/css-syntax-patches-for-csstree": {
"version": "1.1.16", "version": "1.1.15",
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.16.tgz", "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.15.tgz",
"integrity": "sha512-LsDbJPP/B8cPasUJyTA5S99F7C4G93ngW8hUluXz8CFeOaaRnBPtBiIlvgcPYU0HmHNz8CXg+1vwFrIB4F6Dgw==", "integrity": "sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -1581,9 +1581,9 @@
} }
}, },
"node_modules/@oxc-project/types": { "node_modules/@oxc-project/types": {
"version": "0.156.0", "version": "0.155.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.156.0.tgz", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz",
"integrity": "sha512-1Duyg48TdJNdyhEB8Xn1L36bT9Pv6GUtlKjHxOys8xkaF/rQq3XJE+bAurSK/AgIz5mGFsb77cn9Pb20+WCScw==", "integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/oxc-project" "url": "https://github.com/sponsors/oxc-project"
@@ -1679,9 +1679,9 @@
} }
}, },
"node_modules/@rolldown/binding-android-arm-eabi": { "node_modules/@rolldown/binding-android-arm-eabi": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz",
"integrity": "sha512-ia3DwAs0OsMGS7+IConptSiyk9pEXkOx/AbJ2n/lAGxH3QkWQus1t8d7gnAT38FCU2sQu8lUbeR6AiZFv4JxaA==", "integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1695,9 +1695,9 @@
} }
}, },
"node_modules/@rolldown/binding-android-arm64": { "node_modules/@rolldown/binding-android-arm64": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz",
"integrity": "sha512-kx5wBiMB6E7TEvpl2B9gjLU004BkvEm2Dr941BFjx/SontPwSzNuQUIoVKxHHoSjpFIfHMmcguyLOtyn0hObRg==", "integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1711,9 +1711,9 @@
} }
}, },
"node_modules/@rolldown/binding-darwin-arm64": { "node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz",
"integrity": "sha512-qslZrXnKJU4cUpjoAmtAo/KQM9e2IRMlmpig6Zpwj+8KWxW8v+nkAf9216Bsek9lOC1jvg2yCjygCFo2f83msQ==", "integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1727,9 +1727,9 @@
} }
}, },
"node_modules/@rolldown/binding-darwin-x64": { "node_modules/@rolldown/binding-darwin-x64": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz",
"integrity": "sha512-FdF+y48ND13eAyM2mQn+85kvAqNlvRWROy2rhgA463X/0Gv4vGtazZtfaF1mRBLscYvtOlh4QnLjOWiFzX69OQ==", "integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1743,9 +1743,9 @@
} }
}, },
"node_modules/@rolldown/binding-freebsd-x64": { "node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz",
"integrity": "sha512-ddjqFM5e95AgLbUPtKIS7V1KTVlA0RUgCVr5mn+/IEhsdy3pr+Vuj/HZ7nlMN2FyxIwUktOH1LEPIQXO1v9y1Q==", "integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1759,9 +1759,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-arm-gnueabihf": { "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz",
"integrity": "sha512-tHdKWbFkFBI687iim3LIyIvyL7j66VvQYkDPss5Stn/qyE2tWkR1YD0dXmXPFKKqT/o5ZEJVsaRsWS2meQwGOA==", "integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1775,9 +1775,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-arm64-gnu": { "node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz",
"integrity": "sha512-FJiLGAblPBMlBEdbOqw6kW6QcXDWAf/oJu7gYs9ZJZwmg+K+/zjFLxPTIY9izJUzxzc24FHvmseyA0IvJSl17w==", "integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1794,9 +1794,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-arm64-musl": { "node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz",
"integrity": "sha512-hrJpblMsrQ58U9veWpbXzkhV5zStunds6HWLprqq1qauvgBr1E6P+24kLj6lSLI9vjRn/RYpjprhkHQPKGWpNg==", "integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1813,9 +1813,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-ppc64-gnu": { "node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz",
"integrity": "sha512-qJd/dC6j6CwLG9/s/RP2nb1RZ0GIgj7Ioj7x/SYa3RzjVrlzrmH0fhIOpfvqQDeIsdP0loi+kHaMhJzY1PBIaA==", "integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -1832,9 +1832,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-s390x-gnu": { "node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz",
"integrity": "sha512-Vx7Xg5uUJuNcujJjI70OICaH0EYj80BtOLZTSN7uOYg8QOLlyKnaG0IW7Fn/J0IpJsaKzmDKMhOQv1JBM9m9vg==", "integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -1851,9 +1851,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-x64-gnu": { "node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz",
"integrity": "sha512-TNKhgE3uJTsTzatUSSYt0Kgo+Oa2uSlSjz4r1aEIea7HX6ULBym4fAlYUhG1i6pjNf2njfCUAfKwr6C3E9Xg1w==", "integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1870,9 +1870,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-x64-musl": { "node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz",
"integrity": "sha512-7chXdpjXZ8qG0+mIxECN3cT9kvhZ3/oZhMX6Pp876P7CDrIy8nJswva6Mq357JR7eYXfuskwZxMeV1SzoZdHrA==", "integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1889,9 +1889,9 @@
} }
}, },
"node_modules/@rolldown/binding-openharmony-arm64": { "node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz",
"integrity": "sha512-Kwjt+7uNTJK5/MDkGWGVO2KQPnj8yAV75dMpfwyHZrJSXWHrniOB0aUEWlVYf3H1hOqFLIStLdM5QXq6bnKmOQ==", "integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1905,9 +1905,9 @@
} }
}, },
"node_modules/@rolldown/binding-win32-arm64-msvc": { "node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz",
"integrity": "sha512-/LH6WfjDRXXJz/p5xl4TmU+jpcnwcxbKEQBW/GZE0JHfN0lGIyTtsUEGaQ/q/GikkGJ2yLKe6pwm7AnEWO8pdw==", "integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1921,9 +1921,9 @@
} }
}, },
"node_modules/@rolldown/binding-win32-x64-msvc": { "node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.16.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz",
"integrity": "sha512-1lv1KvX0Z871zfk7vw+dnXMMnHNyDwMzWG8Kv3HxDXMOhzCX4SHc7HK319fInP0i+0p9D8brkfzL7pJOaGl+tw==", "integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2158,9 +2158,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.10", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.10.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
"integrity": "sha512-lTyOMnOcmRBzdCv6/4zUZCrEhV0xrqQiUZBZtVcbmLHBXKLnN8q89v/wKRF/Q0HbwVsqbIG3o5HDoFl9sduM+g==", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -2202,9 +2202,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "26.6.6", "version": "26.6.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.6.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.5.tgz",
"integrity": "sha512-MzQj+8Kx3XI40OmUAE6Qoh58ZMNstNNMwn2nfd3qWsfe7nDNSqJNzes4njhz6AHwrSHOffSLwXU6yb474Q4aog==", "integrity": "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~8.9.0" "undici-types": "~8.9.0"
@@ -2796,9 +2796,9 @@
} }
}, },
"node_modules/baseline-browser-mapping": { "node_modules/baseline-browser-mapping": {
"version": "2.11.34", "version": "2.11.33",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.34.tgz", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.33.tgz",
"integrity": "sha512-Aqr8mA1kIvyJ3UY5YAY7ib8LSo1DmDMbMnVqGPtsKW1LxaFQFwn9Eb3yvq2fUKJsRvaXjnAV7R5osqETapzNiA==", "integrity": "sha512-yiVKDX+qCMRKBE084lXvq9EcwFUhKZ2nU8TK1EQQuOgPNR/lmbGxXOeDdlGUqC2Zz11Cs9rk1d/8NfgBmlkt0w==",
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
"baseline-browser-mapping": "dist/cli.cjs" "baseline-browser-mapping": "dist/cli.cjs"
@@ -2831,9 +2831,9 @@
} }
}, },
"node_modules/browserslist": { "node_modules/browserslist": {
"version": "4.29.5", "version": "4.29.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.5.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.4.tgz",
"integrity": "sha512-khTmKu5NGYTotqcYo8Bew2k4RAE9PuXRSFRXG2lQBrCEQHyeQ/vX6gKMxfokwywBsLiXZMnHsmWMG1pnO8J7uQ==", "integrity": "sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -2850,10 +2850,10 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.11.33", "baseline-browser-mapping": "^2.11.29",
"caniuse-lite": "^1.0.30001819", "caniuse-lite": "^1.0.30001816",
"electron-to-chromium": "^1.5.464", "electron-to-chromium": "^1.5.459",
"node-releases": "^2.0.60", "node-releases": "^2.0.59",
"update-browserslist-db": "^1.3.4" "update-browserslist-db": "^1.3.4"
}, },
"bin": { "bin": {
@@ -2898,9 +2898,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001820", "version": "1.0.30001819",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001820.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001819.tgz",
"integrity": "sha512-gfWD6MxqVCqAOwmODfZf7DchWvlrA+SRdNX7ceLfg3xRqe5ssjjpF7EgslC/0tiLiNTajdaNN/q8c/5u/7Gz8w==", "integrity": "sha512-LFUqIIAwpzsIuevnKnsR9qnStBcgzPShTX61lBwN9uFkkf8o8zLiebEjbW8AYtZiF+1YKDWfdt/pWDtMuG8Xwg==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -3245,9 +3245,9 @@
} }
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.472", "version": "1.5.464",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.472.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.464.tgz",
"integrity": "sha512-s4+nfT29b0FKpVKoaIxl2pWsoNcn7A/nMPSrxayw4T3BsmPeV2lF9Y06BsAQeMGhCTQ4nYJ5XST7jkqe4ZEqFw==", "integrity": "sha512-nRa65aFge40viPAKxMKULIY8tcXKwKboJWNn4RoKwyWXJ6SP7BnrDGKUTzc64818jIciH0PK7LSF8QzZes19GA==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
@@ -3258,9 +3258,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/entities": { "node_modules/entities": {
"version": "9.0.0", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-9.0.0.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz",
"integrity": "sha512-oJFNDJg3kTPwAD1cNn2+WmUq4wFoo09npc3Ydf1D7ED4smRiWrsKnaLxykrTWgyGEcusc87P5dxlAleol9kpkg==", "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"engines": { "engines": {
@@ -4274,9 +4274,9 @@
} }
}, },
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "1.4.5", "version": "1.4.3",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.5.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz",
"integrity": "sha512-oLLC2+YvQUfAa+vxoufE/yc1icb87M9iXIRqAoR/2EhE/8cb3SF/+Gi0c2By1B+5Vz2e2vQtUvHiu1+63Znh+w==", "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -4430,9 +4430,9 @@
} }
}, },
"node_modules/node-releases": { "node_modules/node-releases": {
"version": "2.0.63", "version": "2.0.60",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.63.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.60.tgz",
"integrity": "sha512-6li2QanLsAXpIH2R6j77MzRyvOyrvdTqlKtO4CgBdHGNvN99ZZfyxszHc5o5+IZBWD5u6Hzwg83JoGXmqZNG9Q==", "integrity": "sha512-OmO8+GM1JUZqXET720VrJCwkjUCXmCMQm0Z3UDDRbfNLHS4NEAvY60j5jiFv0tkKw2PNNlqOLhahVmwJIOLDIQ==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=18" "node": ">=18"
@@ -4536,9 +4536,9 @@
} }
}, },
"node_modules/ora/node_modules/ansi-regex": { "node_modules/ora/node_modules/ansi-regex": {
"version": "6.5.0", "version": "6.4.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.5.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz",
"integrity": "sha512-zhvbbbdt1kbw7EJ9OQoxmQcuaNR9P5kTj7e/YdmOSU10aHWbycofJxA6jKfoEy3vlLdctOF9ZHaxLtmSm998wQ==", "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -4625,13 +4625,13 @@
} }
}, },
"node_modules/parse5": { "node_modules/parse5": {
"version": "8.1.0", "version": "8.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.1.0.tgz", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
"integrity": "sha512-sq3qBFpee5jeocbFbO48VHlGr8sT7mbXS0vxlKN6sottBtJzrOG3998sr0llSZcQAGgfpdmEYAIZE0P9z0dE8w==", "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"entities": "^9.0.0" "entities": "^8.0.0"
}, },
"funding": { "funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1" "url": "https://github.com/inikulin/parse5?sponsor=1"
@@ -4694,9 +4694,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.32", "version": "8.5.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.32.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz",
"integrity": "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==", "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==",
"devOptional": true, "devOptional": true,
"funding": [ "funding": [
{ {
@@ -5228,12 +5228,12 @@
} }
}, },
"node_modules/rolldown": { "node_modules/rolldown": {
"version": "1.2.16", "version": "1.2.15",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.16.tgz", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz",
"integrity": "sha512-bWvlMpFKiTfDKfnK7FC2MPgPeWCf/Kpwsq49ApdW+FQaBZb6O6dZ4MAPbPUjp7hYQrw/RobqCzn64BRrQecGHw==", "integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@oxc-project/types": "=0.156.0", "@oxc-project/types": "=0.155.0",
"@rolldown/pluginutils": "^1.0.0" "@rolldown/pluginutils": "^1.0.0"
}, },
"bin": { "bin": {
@@ -5243,21 +5243,21 @@
"node": "^20.19.0 || >=22.12.0" "node": "^20.19.0 || >=22.12.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rolldown/binding-android-arm-eabi": "1.2.16", "@rolldown/binding-android-arm-eabi": "1.2.15",
"@rolldown/binding-android-arm64": "1.2.16", "@rolldown/binding-android-arm64": "1.2.15",
"@rolldown/binding-darwin-arm64": "1.2.16", "@rolldown/binding-darwin-arm64": "1.2.15",
"@rolldown/binding-darwin-x64": "1.2.16", "@rolldown/binding-darwin-x64": "1.2.15",
"@rolldown/binding-freebsd-x64": "1.2.16", "@rolldown/binding-freebsd-x64": "1.2.15",
"@rolldown/binding-linux-arm-gnueabihf": "1.2.16", "@rolldown/binding-linux-arm-gnueabihf": "1.2.15",
"@rolldown/binding-linux-arm64-gnu": "1.2.16", "@rolldown/binding-linux-arm64-gnu": "1.2.15",
"@rolldown/binding-linux-arm64-musl": "1.2.16", "@rolldown/binding-linux-arm64-musl": "1.2.15",
"@rolldown/binding-linux-ppc64-gnu": "1.2.16", "@rolldown/binding-linux-ppc64-gnu": "1.2.15",
"@rolldown/binding-linux-s390x-gnu": "1.2.16", "@rolldown/binding-linux-s390x-gnu": "1.2.15",
"@rolldown/binding-linux-x64-gnu": "1.2.16", "@rolldown/binding-linux-x64-gnu": "1.2.15",
"@rolldown/binding-linux-x64-musl": "1.2.16", "@rolldown/binding-linux-x64-musl": "1.2.15",
"@rolldown/binding-openharmony-arm64": "1.2.16", "@rolldown/binding-openharmony-arm64": "1.2.15",
"@rolldown/binding-win32-arm64-msvc": "1.2.16", "@rolldown/binding-win32-arm64-msvc": "1.2.15",
"@rolldown/binding-win32-x64-msvc": "1.2.16" "@rolldown/binding-win32-x64-msvc": "1.2.15"
} }
}, },
"node_modules/saxes": { "node_modules/saxes": {
@@ -5467,9 +5467,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/tinybench": { "node_modules/tinybench": {
"version": "6.2.3", "version": "6.2.2",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.3.tgz", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz",
"integrity": "sha512-Al01C2JIBnO2CXCAPbpAl6sUiOZHeThGtHYNTa51JRw9PyWOCdr+9Ong05R4qg/Sk3+H645E8ov1h2c58ityNw==", "integrity": "sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -5533,9 +5533,9 @@
} }
}, },
"node_modules/tldts-core": { "node_modules/tldts-core": {
"version": "7.4.20", "version": "7.4.19",
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.20.tgz", "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.19.tgz",
"integrity": "sha512-C630TWXZCZmLq0F2bW4QqwnaaxCQ/svpAqvJwnC4JvgEM5ohhBnGsEzTLSOXcN+xjz+kEaHS5uoWIw2klnqd0A==", "integrity": "sha512-goNXV6Ho9TIOfpJZanTd6CKG5DyYgYAL+k+22T0SLh8l6+KHIfBUPFuePuSZv05UttIaaLmI9tb7TmGIdK5AkQ==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -5579,9 +5579,9 @@
} }
}, },
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.9.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.9.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA==", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD" "license": "0BSD"
}, },
"node_modules/tss-react": { "node_modules/tss-react": {

View File

@@ -29,7 +29,7 @@
"@monaco-editor/react": "4.7.0", "@monaco-editor/react": "4.7.0",
"@react-querybuilder/mantine": "8.26.0", "@react-querybuilder/mantine": "8.26.0",
"@reduxjs/toolkit": "2.13.0", "@reduxjs/toolkit": "2.13.0",
"@rolldown/plugin-babel": "0.2.4", "@rolldown/plugin-babel": "0.3.0",
"axios": "1.21.0", "axios": "1.21.0",
"dayjs": "1.11.23", "dayjs": "1.11.23",
"escape-string-regexp": "5.0.0", "escape-string-regexp": "5.0.0",

View File

@@ -10,10 +10,7 @@ import io.quarkus.security.UnauthorizedException;
import jakarta.annotation.Priority; import jakarta.annotation.Priority;
import jakarta.validation.ValidationException; import jakarta.validation.ValidationException;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.NewCookie; import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.core.UriInfo;
import jakarta.ws.rs.ext.Provider; import jakarta.ws.rs.ext.Provider;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -52,19 +49,8 @@ public class ExceptionMappers {
@ServerExceptionMapper(AuthenticationFailedException.class) @ServerExceptionMapper(AuthenticationFailedException.class)
public RestResponse<AuthenticationFailed> authenticationFailed( public RestResponse<AuthenticationFailed> authenticationFailed(
AuthenticationFailedException e, HttpHeaders headers, UriInfo uriInfo) { AuthenticationFailedException e) {
NewCookie logoutCookie = cookieService.buildLogoutCookie(); NewCookie logoutCookie = cookieService.buildLogoutCookie();
// a browser navigating with an invalidated session would otherwise display the error
// instead of the page, retry the request now that the cookie is removed
if (headers.getAcceptableMediaTypes().stream()
.anyMatch(m -> MediaType.TEXT_HTML_TYPE.isCompatible(m) && !m.isWildcardType())) {
return ResponseBuilder.<AuthenticationFailed>create(Status.TEMPORARY_REDIRECT)
.location(uriInfo.getRequestUri())
.cookie(logoutCookie)
.build();
}
return ResponseBuilder.create(Status.UNAUTHORIZED, new AuthenticationFailed(e.getMessage())) return ResponseBuilder.create(Status.UNAUTHORIZED, new AuthenticationFailed(e.getMessage()))
.cookie(logoutCookie) .cookie(logoutCookie)
.build(); .build();

View File

@@ -56,8 +56,6 @@ public class HttpClientFactory {
private static final DnsResolver DNS_RESOLVER = SystemDefaultDnsResolver.INSTANCE; private static final DnsResolver DNS_RESOLVER = SystemDefaultDnsResolver.INSTANCE;
private static final IPAddress CGNAT_RANGE = new IPAddressString("100.64.0.0/10").getAddress(); private static final IPAddress CGNAT_RANGE = new IPAddressString("100.64.0.0/10").getAddress();
private static final IPAddress NAT64_LOCAL_USE_RANGE =
new IPAddressString("64:ff9b:1::/48").getAddress();
private final CommaFeedConfiguration config; private final CommaFeedConfiguration config;
private final CommaFeedVersion version; private final CommaFeedVersion version;
@@ -151,11 +149,7 @@ public class HttpClientFactory {
} }
private static boolean isLocalAddress(IPAddress ip) { private static boolean isLocalAddress(IPAddress ip) {
if (ip.isLocal() if (ip.isLocal() || ip.isLoopback() || ip.isMulticast() || CGNAT_RANGE.contains(ip)) {
|| ip.isLoopback()
|| ip.isMulticast()
|| CGNAT_RANGE.contains(ip)
|| NAT64_LOCAL_USE_RANGE.contains(ip)) {
return true; return true;
} }

View File

@@ -42,9 +42,6 @@ public class User extends AbstractModel {
@Column(nullable = false) @Column(nullable = false)
private boolean disabled; private boolean disabled;
@Column(nullable = false)
private int sessionVersion;
@Column private Instant lastLogin; @Column private Instant lastLogin;
@Column private Instant created; @Column private Instant created;

View File

@@ -155,17 +155,6 @@ public class UserService {
userDAO.delete(user); userDAO.delete(user);
} }
public void changePassword(User user, String password) {
user.setPassword(encryptionService.getEncryptedPassword(password, user.getSalt()));
userDAO.merge(user);
invalidateSessions(user);
}
public void invalidateSessions(User user) {
user.setSessionVersion(user.getSessionVersion() + 1);
}
public String generateApiKey(User user) { public String generateApiKey(User user) {
byte[] key = byte[] key =
encryptionService.getEncryptedPassword( encryptionService.getEncryptedPassword(

View File

@@ -6,6 +6,7 @@ import com.commafeed.backend.dao.UserRoleDAO;
import com.commafeed.backend.model.User; import com.commafeed.backend.model.User;
import com.commafeed.backend.model.UserRole; import com.commafeed.backend.model.UserRole;
import com.commafeed.backend.model.UserRole.Role; import com.commafeed.backend.model.UserRole.Role;
import com.commafeed.backend.service.PasswordEncryptionService;
import com.commafeed.backend.service.UserService; import com.commafeed.backend.service.UserService;
import com.commafeed.frontend.model.UserModel; import com.commafeed.frontend.model.UserModel;
import com.commafeed.frontend.model.request.AdminSaveUserRequest; import com.commafeed.frontend.model.request.AdminSaveUserRequest;
@@ -55,6 +56,7 @@ public class AdminREST {
private final UserDAO userDAO; private final UserDAO userDAO;
private final UserRoleDAO userRoleDAO; private final UserRoleDAO userRoleDAO;
private final UserService userService; private final UserService userService;
private final PasswordEncryptionService encryptionService;
private final MetricRegistry metrics; private final MetricRegistry metrics;
@Path("/user/save") @Path("/user/save")
@@ -100,15 +102,10 @@ public class AdminREST {
User u = userDAO.findById(id); User u = userDAO.findById(id);
u.setName(req.getName()); u.setName(req.getName());
if (StringUtils.isNotBlank(req.getPassword())) { if (StringUtils.isNotBlank(req.getPassword())) {
userService.changePassword(u, req.getPassword()); u.setPassword(
if (StringUtils.isNotBlank(u.getApiKey())) { encryptionService.getEncryptedPassword(req.getPassword(), u.getSalt()));
u.setApiKey(userService.generateApiKey(u));
}
} }
u.setEmail(req.getEmail()); u.setEmail(req.getEmail());
if (!u.isDisabled() && !req.isEnabled()) {
userService.invalidateSessions(u);
}
u.setDisabled(!req.isEnabled()); u.setDisabled(!req.isEnabled());
Set<Role> roles = userRoleDAO.findRoles(u); Set<Role> roles = userRoleDAO.findRoles(u);

View File

@@ -21,6 +21,7 @@ import com.commafeed.backend.model.UserSettings.ReadingMode;
import com.commafeed.backend.model.UserSettings.ReadingOrder; import com.commafeed.backend.model.UserSettings.ReadingOrder;
import com.commafeed.backend.model.UserSettings.ScrollMode; import com.commafeed.backend.model.UserSettings.ScrollMode;
import com.commafeed.backend.service.MailService; import com.commafeed.backend.service.MailService;
import com.commafeed.backend.service.PasswordEncryptionService;
import com.commafeed.backend.service.PushNotificationService; import com.commafeed.backend.service.PushNotificationService;
import com.commafeed.backend.service.UserService; import com.commafeed.backend.service.UserService;
import com.commafeed.backend.service.db.DatabaseStartupService; import com.commafeed.backend.service.db.DatabaseStartupService;
@@ -35,7 +36,6 @@ import com.commafeed.frontend.model.request.RegistrationRequest;
import com.commafeed.security.AuthenticationContext; import com.commafeed.security.AuthenticationContext;
import com.commafeed.security.Roles; import com.commafeed.security.Roles;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.html.HtmlEscapers;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
@@ -86,6 +86,7 @@ public class UserREST {
private final UserRoleDAO userRoleDAO; private final UserRoleDAO userRoleDAO;
private final UserSettingsDAO userSettingsDAO; private final UserSettingsDAO userSettingsDAO;
private final UserService userService; private final UserService userService;
private final PasswordEncryptionService encryptionService;
private final DatabaseStartupService databaseStartupService; private final DatabaseStartupService databaseStartupService;
private final MailService mailService; private final MailService mailService;
private final CommaFeedConfiguration config; private final CommaFeedConfiguration config;
@@ -315,7 +316,10 @@ public class UserREST {
} }
if (StringUtils.isNotBlank(request.getNewPassword())) { if (StringUtils.isNotBlank(request.getNewPassword())) {
userService.changePassword(user, request.getNewPassword()); byte[] password =
encryptionService.getEncryptedPassword(
request.getNewPassword(), user.getSalt());
user.setPassword(password);
user.setApiKey(userService.generateApiKey(user)); user.setApiKey(userService.generateApiKey(user));
} }
@@ -413,7 +417,7 @@ public class UserREST {
Urls.removeTrailingSlash(config.passwordRecoveryPublicBaseUrl().orElseThrow()); Urls.removeTrailingSlash(config.passwordRecoveryPublicBaseUrl().orElseThrow());
return String.format( return String.format(
"You asked for password recovery for account '%s', <a href='%s'>follow this link</a> to change your password. Ignore this if you didn't request a password recovery.", "You asked for password recovery for account '%s', <a href='%s'>follow this link</a> to change your password. Ignore this if you didn't request a password recovery.",
HtmlEscapers.htmlEscaper().escape(user.getName()), callbackUrl(user, publicUrl)); user.getName(), callbackUrl(user, publicUrl));
} }
private String callbackUrl(User user, String publicUrl) throws URISyntaxException { private String callbackUrl(User user, String publicUrl) throws URISyntaxException {
@@ -451,7 +455,8 @@ public class UserREST {
return Response.status(Status.UNAUTHORIZED).entity("Token expired.").build(); return Response.status(Status.UNAUTHORIZED).entity("Token expired.").build();
} }
userService.changePassword(user, password); byte[] encryptedPassword = encryptionService.getEncryptedPassword(password, user.getSalt());
user.setPassword(encryptedPassword);
if (StringUtils.isNotBlank(user.getApiKey())) { if (StringUtils.isNotBlank(user.getApiKey())) {
user.setApiKey(userService.generateApiKey(user)); user.setApiKey(userService.generateApiKey(user));
} }

View File

@@ -21,11 +21,11 @@ public class AuthenticationContext {
return null; return null;
} }
String name = securityIdentity.getPrincipal().getName(); String userId = securityIdentity.getPrincipal().getName();
if (name == null) { if (userId == null) {
return null; return null;
} }
return userDAO.findById(SessionPrincipal.parse(name).userId()); return userDAO.findById(Long.valueOf(userId));
} }
} }

View File

@@ -1,27 +0,0 @@
package com.commafeed.security;
import com.commafeed.backend.model.User;
/** Value of the security principal, which is also what ends up in the encrypted session cookie. */
public record SessionPrincipal(long userId, int sessionVersion) {
private static final String SEPARATOR = ":";
public static SessionPrincipal of(User user) {
return new SessionPrincipal(user.getId(), user.getSessionVersion());
}
public static SessionPrincipal parse(String name) {
// cookies created before session versions were introduced only contain the user id
if (!name.contains(SEPARATOR)) {
return new SessionPrincipal(Long.parseLong(name), 0);
}
String[] parts = name.split(SEPARATOR, 2);
return new SessionPrincipal(Long.parseLong(parts[0]), Integer.parseInt(parts[1]));
}
public String toName() {
return userId + SEPARATOR + sessionVersion;
}
}

View File

@@ -4,7 +4,6 @@ import com.commafeed.backend.dao.UnitOfWork;
import com.commafeed.backend.model.User; import com.commafeed.backend.model.User;
import com.commafeed.backend.model.UserRole.Role; import com.commafeed.backend.model.UserRole.Role;
import com.commafeed.backend.service.UserService; import com.commafeed.backend.service.UserService;
import com.commafeed.security.SessionPrincipal;
import io.quarkus.security.AuthenticationFailedException; import io.quarkus.security.AuthenticationFailedException;
import io.quarkus.security.identity.AuthenticationRequestContext; import io.quarkus.security.identity.AuthenticationRequestContext;
@@ -48,7 +47,7 @@ public class DatabaseApiKeyIdentityProvider
Set<Role> roles = unitOfWork.call(() -> userService.getRoles(user.get())); Set<Role> roles = unitOfWork.call(() -> userService.getRoles(user.get()));
return QuarkusSecurityIdentity.builder() return QuarkusSecurityIdentity.builder()
.setPrincipal(new QuarkusPrincipal(SessionPrincipal.of(user.get()).toName())) .setPrincipal(new QuarkusPrincipal(String.valueOf(user.get().getId())))
.addRoles(roles.stream().map(Enum::name).collect(Collectors.toSet())) .addRoles(roles.stream().map(Enum::name).collect(Collectors.toSet()))
.build(); .build();
}); });

View File

@@ -6,7 +6,6 @@ import com.commafeed.backend.model.UserRole.Role;
import com.commafeed.backend.service.UserService; import com.commafeed.backend.service.UserService;
import com.commafeed.frontend.exception.CommaFeedApplicationException; import com.commafeed.frontend.exception.CommaFeedApplicationException;
import com.commafeed.frontend.exception.CommaFeedExceptionType; import com.commafeed.frontend.exception.CommaFeedExceptionType;
import com.commafeed.security.SessionPrincipal;
import io.quarkus.security.identity.AuthenticationRequestContext; import io.quarkus.security.identity.AuthenticationRequestContext;
import io.quarkus.security.identity.IdentityProvider; import io.quarkus.security.identity.IdentityProvider;
@@ -53,7 +52,7 @@ public class DatabaseUsernamePasswordIdentityProvider
Set<Role> roles = unitOfWork.call(() -> userService.getRoles(user.get())); Set<Role> roles = unitOfWork.call(() -> userService.getRoles(user.get()));
return QuarkusSecurityIdentity.builder() return QuarkusSecurityIdentity.builder()
.setPrincipal(new QuarkusPrincipal(SessionPrincipal.of(user.get()).toName())) .setPrincipal(new QuarkusPrincipal(String.valueOf(user.get().getId())))
.addRoles(roles.stream().map(Enum::name).collect(Collectors.toSet())) .addRoles(roles.stream().map(Enum::name).collect(Collectors.toSet()))
.build(); .build();
}); });

View File

@@ -6,7 +6,6 @@ import com.commafeed.backend.model.User;
import com.commafeed.backend.model.UserRole.Role; import com.commafeed.backend.model.UserRole.Role;
import com.commafeed.backend.service.UserService; import com.commafeed.backend.service.UserService;
import com.commafeed.backend.service.internal.PostLoginActivities; import com.commafeed.backend.service.internal.PostLoginActivities;
import com.commafeed.security.SessionPrincipal;
import io.quarkus.security.AuthenticationFailedException; import io.quarkus.security.AuthenticationFailedException;
import io.quarkus.security.identity.AuthenticationRequestContext; import io.quarkus.security.identity.AuthenticationRequestContext;
@@ -42,17 +41,11 @@ public class TrustedIdentityProvider implements IdentityProvider<TrustedAuthenti
public Uni<SecurityIdentity> authenticate( public Uni<SecurityIdentity> authenticate(
TrustedAuthenticationRequest request, AuthenticationRequestContext context) { TrustedAuthenticationRequest request, AuthenticationRequestContext context) {
return context.runBlocking(() -> { return context.runBlocking(() -> {
SessionPrincipal principal = SessionPrincipal.parse(request.getPrincipal()); Long userId = Long.valueOf(request.getPrincipal());
User user = unitOfWork.call(() -> userDAO.findById(principal.userId())); User user = unitOfWork.call(() -> userDAO.findById(userId));
if (user == null) { if (user == null) {
throw new AuthenticationFailedException("user not found"); throw new AuthenticationFailedException("user not found");
} }
if (user.isDisabled()) {
throw new AuthenticationFailedException("user disabled");
}
if (user.getSessionVersion() != principal.sessionVersion()) {
throw new AuthenticationFailedException("session invalidated");
}
// execute post login activities manually because we didn't call login() since // execute post login activities manually because we didn't call login() since
// we received // we received
@@ -61,7 +54,7 @@ public class TrustedIdentityProvider implements IdentityProvider<TrustedAuthenti
Set<Role> roles = unitOfWork.call(() -> userService.getRoles(user)); Set<Role> roles = unitOfWork.call(() -> userService.getRoles(user));
return QuarkusSecurityIdentity.builder() return QuarkusSecurityIdentity.builder()
.setPrincipal(new QuarkusPrincipal(principal.toName())) .setPrincipal(new QuarkusPrincipal(String.valueOf(userId)))
.addRoles(roles.stream().map(Enum::name).collect(Collectors.toSet())) .addRoles(roles.stream().map(Enum::name).collect(Collectors.toSet()))
.build(); .build();
}); });

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="user-session-version" author="athou">
<addColumn tableName="USERS">
<column name="sessionVersion" type="INT" valueNumeric="0" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

View File

@@ -39,6 +39,5 @@
<include file="changelogs/db.changelog-5.12.xml" /> <include file="changelogs/db.changelog-5.12.xml" />
<include file="changelogs/db.changelog-7.0.xml" /> <include file="changelogs/db.changelog-7.0.xml" />
<include file="changelogs/db.changelog-7.2.xml" /> <include file="changelogs/db.changelog-7.2.xml" />
<include file="changelogs/db.changelog-7.5.xml" />
</databaseChangeLog> </databaseChangeLog>

View File

@@ -561,17 +561,6 @@ class HttpGetterTest {
() -> getter.get("http://[64:ff9b::a00:1]")); // 10.0.0.1 () -> getter.get("http://[64:ff9b::a00:1]")); // 10.0.0.1
} }
@Test
void nat64LocalUse() {
// 64:ff9b:1::/48 is reserved for local use (RFC 8215), the whole range is blocked
Assertions.assertThrows(
UnknownHostException.class,
() -> getter.get("http://[64:ff9b:1::a9fe:a9fe]")); // 169.254.169.254
Assertions.assertThrows(
UnknownHostException.class,
() -> getter.get("http://[64:ff9b:1:ffff::a00:1]")); // 10.0.0.1
}
@Test @Test
void sixToFour() { void sixToFour() {
// 2002::/16 embeds the IPv4 address in bits 16-47 (RFC 3056) // 2002::/16 embeds the IPv4 address in bits 16-47 (RFC 3056)

View File

@@ -109,15 +109,15 @@ public abstract class BaseIT {
} }
protected List<HttpCookie> login() { protected List<HttpCookie> login() {
return login(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
}
protected List<HttpCookie> login(String userName, String password) {
List<Header> setCookieHeaders = List<Header> setCookieHeaders =
RestAssured.given() RestAssured.given()
.auth() .auth()
.none() .none()
.formParams("j_username", userName, "j_password", password) .formParams(
"j_username",
TestConstants.ADMIN_USERNAME,
"j_password",
TestConstants.ADMIN_PASSWORD)
.post("j_security_check") .post("j_security_check")
.then() .then()
.statusCode(HttpStatus.SC_OK) .statusCode(HttpStatus.SC_OK)

View File

@@ -6,7 +6,6 @@ import com.commafeed.TestConstants;
import com.commafeed.frontend.exception.CommaFeedExceptionType; import com.commafeed.frontend.exception.CommaFeedExceptionType;
import com.commafeed.frontend.model.Entries; import com.commafeed.frontend.model.Entries;
import com.commafeed.frontend.model.UserModel; import com.commafeed.frontend.model.UserModel;
import com.commafeed.frontend.model.request.AdminSaveUserRequest;
import com.commafeed.frontend.model.request.MarkRequest; import com.commafeed.frontend.model.request.MarkRequest;
import com.commafeed.frontend.model.request.ProfileModificationRequest; import com.commafeed.frontend.model.request.ProfileModificationRequest;
import com.commafeed.frontend.model.request.SubscribeRequest; import com.commafeed.frontend.model.request.SubscribeRequest;
@@ -18,7 +17,6 @@ import io.restassured.http.ContentType;
import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.HttpHeaders;
import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpStatus;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -30,10 +28,6 @@ import java.util.stream.Collectors;
@QuarkusTest @QuarkusTest
class SecurityIT extends BaseIT { class SecurityIT extends BaseIT {
private static final String USER_NAME = "session-user";
private static final String USER_PASSWORD = "MyPassword123!";
private static final String NEW_USER_PASSWORD = "MyNewPassword123!";
@BeforeEach @BeforeEach
void setup() { void setup() {
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD); initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
@@ -190,232 +184,4 @@ class SecurityIT extends BaseIT {
.then() .then()
.statusCode(HttpStatus.SC_UNAUTHORIZED); .statusCode(HttpStatus.SC_UNAUTHORIZED);
} }
@Test
void selfPasswordChangeInvalidatesSessions() {
createUser(false);
String sessionA = session(USER_NAME, USER_PASSWORD);
String sessionB = session(USER_NAME, USER_PASSWORD);
String adminSession = session(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
ProfileModificationRequest req = new ProfileModificationRequest();
req.setCurrentPassword(USER_PASSWORD);
req.setNewPassword(NEW_USER_PASSWORD);
RestAssured.given()
.header(HttpHeaders.COOKIE, sessionA)
.body(req)
.contentType(ContentType.JSON)
.post("rest/user/profile")
.then()
.statusCode(HttpStatus.SC_OK);
assertSessionStatus(sessionA, HttpStatus.SC_UNAUTHORIZED);
assertSessionStatus(sessionB, HttpStatus.SC_UNAUTHORIZED);
assertSessionStatus(adminSession, HttpStatus.SC_OK);
assertSessionStatus(session(USER_NAME, NEW_USER_PASSWORD), HttpStatus.SC_OK);
}
@Test
void profileChangeWithoutNewPasswordKeepsSessions() {
createUser(false);
String session = session(USER_NAME, USER_PASSWORD);
ProfileModificationRequest req = new ProfileModificationRequest();
req.setCurrentPassword(USER_PASSWORD);
req.setEmail("session-user@commafeed.com");
RestAssured.given()
.header(HttpHeaders.COOKIE, session)
.body(req)
.contentType(ContentType.JSON)
.post("rest/user/profile")
.then()
.statusCode(HttpStatus.SC_OK);
assertSessionStatus(session, HttpStatus.SC_OK);
}
@Test
void adminPasswordChangeInvalidatesSessionsAndRotatesApiKey() {
Long id = createUser(false);
String session = session(USER_NAME, USER_PASSWORD);
ProfileModificationRequest apiKeyReq = new ProfileModificationRequest();
apiKeyReq.setCurrentPassword(USER_PASSWORD);
apiKeyReq.setNewApiKey(true);
RestAssured.given()
.header(HttpHeaders.COOKIE, session)
.body(apiKeyReq)
.contentType(ContentType.JSON)
.post("rest/user/profile")
.then()
.statusCode(HttpStatus.SC_OK);
String apiKey = profile(session).getApiKey();
Assertions.assertNotNull(apiKey);
AdminSaveUserRequest req = saveUserRequest(false);
req.setId(id);
req.setPassword(NEW_USER_PASSWORD);
saveUser(req);
assertSessionStatus(session, HttpStatus.SC_UNAUTHORIZED);
String newSession = session(USER_NAME, NEW_USER_PASSWORD);
assertSessionStatus(newSession, HttpStatus.SC_OK);
Assertions.assertNotEquals(apiKey, profile(newSession).getApiKey());
}
@Test
void adminPasswordChangeDoesNotCreateApiKey() {
Long id = createUser(false);
AdminSaveUserRequest req = saveUserRequest(false);
req.setId(id);
req.setPassword(NEW_USER_PASSWORD);
saveUser(req);
Assertions.assertNull(profile(session(USER_NAME, NEW_USER_PASSWORD)).getApiKey());
}
@Test
void disablingUserInvalidatesSessions() {
Long id = createUser(false);
String session = session(USER_NAME, USER_PASSWORD);
assertSessionStatus(session, HttpStatus.SC_OK);
AdminSaveUserRequest req = saveUserRequest(false);
req.setId(id);
req.setEnabled(false);
saveUser(req);
assertSessionStatus(session, HttpStatus.SC_UNAUTHORIZED);
// sessions opened before the user was disabled stay invalid after enabling it again
req.setEnabled(true);
saveUser(req);
assertSessionStatus(session, HttpStatus.SC_UNAUTHORIZED);
assertSessionStatus(session(USER_NAME, USER_PASSWORD), HttpStatus.SC_OK);
}
@Test
void savingUserWithoutChangesKeepsSessions() {
Long id = createUser(false);
String session = session(USER_NAME, USER_PASSWORD);
AdminSaveUserRequest req = saveUserRequest(false);
req.setId(id);
saveUser(req);
assertSessionStatus(session, HttpStatus.SC_OK);
}
@Test
void disablingAdminRevokesAdminAccess() {
Long id = createUser(true);
String session = session(USER_NAME, USER_PASSWORD);
RestAssured.given()
.header(HttpHeaders.COOKIE, session)
.get("rest/admin/metrics")
.then()
.statusCode(HttpStatus.SC_OK);
AdminSaveUserRequest req = saveUserRequest(true);
req.setId(id);
req.setEnabled(false);
saveUser(req);
RestAssured.given()
.header(HttpHeaders.COOKIE, session)
.get("rest/admin/metrics")
.then()
.statusCode(HttpStatus.SC_UNAUTHORIZED);
}
@Test
void invalidatedSessionRemovesCookie() {
Long id = createUser(false);
String session = session(USER_NAME, USER_PASSWORD);
AdminSaveUserRequest req = saveUserRequest(false);
req.setId(id);
req.setEnabled(false);
saveUser(req);
// api calls receive an error
String apiSetCookie =
RestAssured.given()
.header(HttpHeaders.COOKIE, session)
.get("rest/user/profile")
.then()
.statusCode(HttpStatus.SC_UNAUTHORIZED)
.extract()
.header(HttpHeaders.SET_COOKIE);
Assertions.assertTrue(apiSetCookie.contains("Max-Age=0"));
// browsers are redirected to the page they asked for, this time without the cookie
String browserSetCookie =
RestAssured.given()
.redirects()
.follow(false)
.header(HttpHeaders.COOKIE, session)
.header(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,*/*;q=0.8")
.get("next?category=all")
.then()
.statusCode(HttpStatus.SC_TEMPORARY_REDIRECT)
.header(HttpHeaders.LOCATION, Matchers.endsWith("/next?category=all"))
.extract()
.header(HttpHeaders.SET_COOKIE);
Assertions.assertTrue(browserSetCookie.contains("Max-Age=0"));
}
private Long createUser(boolean admin) {
AdminSaveUserRequest req = saveUserRequest(admin);
req.setPassword(USER_PASSWORD);
return saveUser(req);
}
private AdminSaveUserRequest saveUserRequest(boolean admin) {
AdminSaveUserRequest req = new AdminSaveUserRequest();
req.setName(USER_NAME);
req.setEnabled(true);
req.setAdmin(admin);
return req;
}
private Long saveUser(AdminSaveUserRequest req) {
return RestAssured.given()
.auth()
.preemptive()
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
.body(req)
.contentType(ContentType.JSON)
.post("rest/admin/user/save")
.then()
.statusCode(HttpStatus.SC_OK)
.extract()
.as(Long.class);
}
private String session(String userName, String password) {
return login(userName, password).stream()
.map(HttpCookie::toString)
.collect(Collectors.joining(";"));
}
private UserModel profile(String session) {
return RestAssured.given()
.header(HttpHeaders.COOKIE, session)
.get("rest/user/profile")
.then()
.statusCode(HttpStatus.SC_OK)
.extract()
.as(UserModel.class);
}
private void assertSessionStatus(String session, int status) {
RestAssured.given()
.header(HttpHeaders.COOKIE, session)
.get("rest/user/profile")
.then()
.statusCode(status);
}
} }

View File

@@ -4,7 +4,6 @@ import com.commafeed.TestConstants;
import com.commafeed.frontend.model.Settings; import com.commafeed.frontend.model.Settings;
import com.commafeed.frontend.model.request.PasswordResetConfirmationRequest; import com.commafeed.frontend.model.request.PasswordResetConfirmationRequest;
import com.commafeed.frontend.model.request.PasswordResetRequest; import com.commafeed.frontend.model.request.PasswordResetRequest;
import com.commafeed.frontend.model.request.RegistrationRequest;
import com.commafeed.integration.BaseIT; import com.commafeed.integration.BaseIT;
import io.quarkus.mailer.MockMailbox; import io.quarkus.mailer.MockMailbox;
@@ -14,21 +13,17 @@ import io.restassured.http.ContentType;
import io.vertx.ext.mail.MailMessage; import io.vertx.ext.mail.MailMessage;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.ws.rs.core.HttpHeaders;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.net.HttpCookie;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
@QuarkusTest @QuarkusTest
class UserIT extends BaseIT { class UserIT extends BaseIT {
@@ -52,9 +47,6 @@ class UserIT extends BaseIT {
@Test @Test
void resetPassword() { void resetPassword() {
String session =
login().stream().map(HttpCookie::toString).collect(Collectors.joining(";"));
PasswordResetRequest req = new PasswordResetRequest(); PasswordResetRequest req = new PasswordResetRequest();
req.setEmail("admin@commafeed.com"); req.setEmail("admin@commafeed.com");
RestAssured.given() RestAssured.given()
@@ -107,59 +99,11 @@ class UserIT extends BaseIT {
.then() .then()
.statusCode(200); .statusCode(200);
// sessions opened before the reset are no longer valid
RestAssured.given()
.auth()
.none()
.header(HttpHeaders.COOKIE, session)
.get("rest/user/settings")
.then()
.statusCode(401);
RestAssured.authentication = RestAssured.authentication =
RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, newPassword); RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, newPassword);
RestAssured.given().get("rest/user/settings").then().statusCode(200); RestAssured.given().get("rest/user/settings").then().statusCode(200);
} }
@Test
void resetPasswordEscapesUsernameInEmail() {
String name = "<a href=//evil.example>CLICK</a>";
String email = "victim@commafeed.com";
RegistrationRequest registrationReq = new RegistrationRequest();
registrationReq.setName(name);
registrationReq.setPassword("MyPassword123!");
registrationReq.setEmail(email);
RestAssured.given()
.body(registrationReq)
.contentType(ContentType.JSON)
.post("rest/user/register")
.then()
.statusCode(200);
PasswordResetRequest req = new PasswordResetRequest();
req.setEmail(email);
RestAssured.given()
.body(req)
.contentType(ContentType.JSON)
.post("rest/user/passwordReset")
.then()
.statusCode(200);
List<MailMessage> mails = mailbox.getMailMessagesSentTo(email);
Assertions.assertEquals(1, mails.size());
String html = mails.getFirst().getHtml();
Assertions.assertTrue(
html.startsWith(
"You asked for password recovery for account '&lt;a href=//evil.example&gt;CLICK&lt;/a&gt;'"));
Elements links = Jsoup.parse(html).select("a");
Assertions.assertEquals(1, links.size());
Assertions.assertTrue(
links.getFirst().attr("href").startsWith("https://commafeed.example.com"));
}
@Test @Test
void saveSettings() { void saveSettings() {
Settings settings = Settings settings =

View File

@@ -1,19 +0,0 @@
package com.commafeed.security;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class SessionPrincipalTest {
@Test
void roundTrip() {
SessionPrincipal principal = new SessionPrincipal(12, 3);
Assertions.assertEquals("12:3", principal.toName());
Assertions.assertEquals(principal, SessionPrincipal.parse(principal.toName()));
}
@Test
void cookieWithoutSessionVersion() {
Assertions.assertEquals(new SessionPrincipal(12, 0), SessionPrincipal.parse("12"));
}
}