Compare commits

...
12 Commits
Author SHA1 Message Date
Stavros 50c25e4478 fix: fix domain validator with port but no scheme and https or http 2026-07-18 18:17:24 +03:00
Stavros d6087dc45e fix: allow for mismatching protocol in trusted domain check - ui warnings 2026-07-18 18:06:26 +03:00
Stavros 6efb970673 fix: fix lint errors 2026-07-18 17:52:08 +03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a9221ece92 chore(deps): bump actions/setup-go from 6.5.0 to 7.0.0 (#1012)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-18 17:10:09 +03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d5b7e60362 chore(deps): bump the minor-patch group across 1 directory with 4 updates (#1020)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-18 17:09:38 +03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4d8cd86eec chore(deps): bump the minor-patch group with 2 updates (#1018)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-18 17:09:17 +03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
504866a432 chore(deps): bump github/codeql-action/upload-sarif from 4.37.0 to 4.37.1 (#1019)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-18 17:08:27 +03:00
Stavros 0c1a64d47d fix: make login_for optional 2026-07-18 17:05:52 +03:00
Stavros a79438d6ec fix: fix typo 2026-07-18 16:47:53 +03:00
Stavros 286aad2e08 test: remove obsolete test case 2026-07-18 16:37:52 +03:00
Stavros 5091fc2d7a fix: suppress irrelevant debugs logs in acls service 2026-07-18 16:30:12 +03:00
Stavros 0fc87ad58f fix: support for parent trusted domain, fixes #1021 2026-07-18 16:30:12 +03:00
26 changed files with 428 additions and 239 deletions
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
package_json_file: ./frontend/package.json package_json_file: ./frontend/package.json
- name: Setup go - name: Setup go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6 uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with: with:
go-version: "^1.26.4" go-version: "^1.26.4"
+2 -2
View File
@@ -68,7 +68,7 @@ jobs:
package_json_file: ./frontend/package.json package_json_file: ./frontend/package.json
- name: Install go - name: Install go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6 uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with: with:
go-version: "^1.26.4" go-version: "^1.26.4"
@@ -117,7 +117,7 @@ jobs:
package_json_file: ./frontend/package.json package_json_file: ./frontend/package.json
- name: Install go - name: Install go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6 uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with: with:
go-version: "^1.26.4" go-version: "^1.26.4"
+2 -2
View File
@@ -46,7 +46,7 @@ jobs:
package_json_file: ./frontend/package.json package_json_file: ./frontend/package.json
- name: Install go - name: Install go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6 uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with: with:
go-version: "^1.26.4" go-version: "^1.26.4"
@@ -92,7 +92,7 @@ jobs:
package_json_file: ./frontend/package.json package_json_file: ./frontend/package.json
- name: Install go - name: Install go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6 uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with: with:
go-version: "^1.26.4" go-version: "^1.26.4"
+1 -1
View File
@@ -38,6 +38,6 @@ jobs:
retention-days: 5 retention-days: 5
- name: Upload to code-scanning - name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4 uses: github/codeql-action/upload-sarif@7188fc363630916deb702c7fdcf4e481b751f97a # v4
with: with:
sarif_file: results.sarif sarif_file: results.sarif
+4 -4
View File
@@ -32,7 +32,7 @@
"react": "^19.2.7", "react": "^19.2.7",
"react-dom": "^19.2.7", "react-dom": "^19.2.7",
"react-hook-form": "^7.81.0", "react-hook-form": "^7.81.0",
"react-i18next": "^17.0.9", "react-i18next": "^17.0.10",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"react-router": "^8.2.0", "react-router": "^8.2.0",
"sonner": "^2.0.7", "sonner": "^2.0.7",
@@ -47,14 +47,14 @@
"@types/react": "^19.2.17", "@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.3", "@vitejs/plugin-react": "^6.0.3",
"eslint": "^10.6.0", "eslint": "^10.7.0",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.3", "eslint-plugin-react-refresh": "^0.5.3",
"globals": "^17.7.0", "globals": "^17.7.0",
"rollup-plugin-visualizer": "^7.0.1", "rollup-plugin-visualizer": "^7.0.1",
"tw-animate-css": "^1.4.0", "tw-animate-css": "^1.4.0",
"typescript": "~6.0.2", "typescript": "~6.0.2",
"typescript-eslint": "^8.63.0", "typescript-eslint": "^8.64.0",
"vite": "^8.1.4" "vite": "^8.1.5"
} }
} }
+122 -122
View File
@@ -28,7 +28,7 @@ importers:
version: 1.3.0(@types/react@19.2.17)(react@19.2.7) version: 1.3.0(@types/react@19.2.17)(react@19.2.7)
'@tailwindcss/vite': '@tailwindcss/vite':
specifier: ^4.3.2 specifier: ^4.3.2
version: 4.3.2(vite@8.1.4(@types/node@26.1.1)(jiti@2.7.0)) version: 4.3.2(vite@8.1.5(@types/node@26.1.1)(jiti@2.7.0))
'@tanstack/react-query': '@tanstack/react-query':
specifier: ^5.101.2 specifier: ^5.101.2
version: 5.101.2(react@19.2.7) version: 5.101.2(react@19.2.7)
@@ -69,8 +69,8 @@ importers:
specifier: ^7.81.0 specifier: ^7.81.0
version: 7.81.0(react@19.2.7) version: 7.81.0(react@19.2.7)
react-i18next: react-i18next:
specifier: ^17.0.9 specifier: ^17.0.10
version: 17.0.9(i18next@26.3.6(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) version: 17.0.10(i18next@26.3.6(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)
react-markdown: react-markdown:
specifier: ^10.1.0 specifier: ^10.1.0
version: 10.1.0(@types/react@19.2.17)(react@19.2.7) version: 10.1.0(@types/react@19.2.17)(react@19.2.7)
@@ -92,10 +92,10 @@ importers:
devDependencies: devDependencies:
'@eslint/js': '@eslint/js':
specifier: ^10.0.1 specifier: ^10.0.1
version: 10.0.1(eslint@10.6.0(jiti@2.7.0)) version: 10.0.1(eslint@10.7.0(jiti@2.7.0))
'@tanstack/eslint-plugin-query': '@tanstack/eslint-plugin-query':
specifier: ^5.101.2 specifier: ^5.101.2
version: 5.101.2(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) version: 5.101.2(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)
'@types/node': '@types/node':
specifier: ^26.1.1 specifier: ^26.1.1
version: 26.1.1 version: 26.1.1
@@ -107,16 +107,16 @@ importers:
version: 19.2.3(@types/react@19.2.17) version: 19.2.3(@types/react@19.2.17)
'@vitejs/plugin-react': '@vitejs/plugin-react':
specifier: ^6.0.3 specifier: ^6.0.3
version: 6.0.3(vite@8.1.4(@types/node@26.1.1)(jiti@2.7.0)) version: 6.0.3(vite@8.1.5(@types/node@26.1.1)(jiti@2.7.0))
eslint: eslint:
specifier: ^10.6.0 specifier: ^10.7.0
version: 10.6.0(jiti@2.7.0) version: 10.7.0(jiti@2.7.0)
eslint-plugin-react-hooks: eslint-plugin-react-hooks:
specifier: ^7.0.1 specifier: ^7.0.1
version: 7.1.1(eslint@10.6.0(jiti@2.7.0)) version: 7.1.1(eslint@10.7.0(jiti@2.7.0))
eslint-plugin-react-refresh: eslint-plugin-react-refresh:
specifier: ^0.5.3 specifier: ^0.5.3
version: 0.5.3(eslint@10.6.0(jiti@2.7.0)) version: 0.5.3(eslint@10.7.0(jiti@2.7.0))
globals: globals:
specifier: ^17.7.0 specifier: ^17.7.0
version: 17.7.0 version: 17.7.0
@@ -130,11 +130,11 @@ importers:
specifier: ~6.0.2 specifier: ~6.0.2
version: 6.0.3 version: 6.0.3
typescript-eslint: typescript-eslint:
specifier: ^8.63.0 specifier: ^8.64.0
version: 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) version: 8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)
vite: vite:
specifier: ^8.1.4 specifier: ^8.1.5
version: 8.1.4(@types/node@26.1.1)(jiti@2.7.0) version: 8.1.5(@types/node@26.1.1)(jiti@2.7.0)
packages: packages:
@@ -1272,16 +1272,16 @@ packages:
'@types/unist@3.0.3': '@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
'@typescript-eslint/eslint-plugin@8.63.0': '@typescript-eslint/eslint-plugin@8.64.0':
resolution: {integrity: sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ==} resolution: {integrity: sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
'@typescript-eslint/parser': ^8.63.0 '@typescript-eslint/parser': ^8.64.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/parser@8.63.0': '@typescript-eslint/parser@8.64.0':
resolution: {integrity: sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig==} resolution: {integrity: sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
@@ -1293,8 +1293,8 @@ packages:
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/project-service@8.63.0': '@typescript-eslint/project-service@8.64.0':
resolution: {integrity: sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==} resolution: {integrity: sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
@@ -1303,8 +1303,8 @@ packages:
resolution: {integrity: sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==} resolution: {integrity: sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/scope-manager@8.63.0': '@typescript-eslint/scope-manager@8.64.0':
resolution: {integrity: sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==} resolution: {integrity: sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.62.1': '@typescript-eslint/tsconfig-utils@8.62.1':
@@ -1313,14 +1313,14 @@ packages:
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/tsconfig-utils@8.63.0': '@typescript-eslint/tsconfig-utils@8.64.0':
resolution: {integrity: sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==} resolution: {integrity: sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/type-utils@8.63.0': '@typescript-eslint/type-utils@8.64.0':
resolution: {integrity: sha512-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA==} resolution: {integrity: sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
@@ -1330,8 +1330,8 @@ packages:
resolution: {integrity: sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==} resolution: {integrity: sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/types@8.63.0': '@typescript-eslint/types@8.64.0':
resolution: {integrity: sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==} resolution: {integrity: sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.62.1': '@typescript-eslint/typescript-estree@8.62.1':
@@ -1340,8 +1340,8 @@ packages:
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/typescript-estree@8.63.0': '@typescript-eslint/typescript-estree@8.64.0':
resolution: {integrity: sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==} resolution: {integrity: sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
@@ -1353,8 +1353,8 @@ packages:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/utils@8.63.0': '@typescript-eslint/utils@8.64.0':
resolution: {integrity: sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==} resolution: {integrity: sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
@@ -1364,8 +1364,8 @@ packages:
resolution: {integrity: sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==} resolution: {integrity: sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/visitor-keys@8.63.0': '@typescript-eslint/visitor-keys@8.64.0':
resolution: {integrity: sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==} resolution: {integrity: sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@ungap/structured-clone@1.3.1': '@ungap/structured-clone@1.3.1':
@@ -1603,8 +1603,8 @@ packages:
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24} engines: {node: ^20.19.0 || ^22.13.0 || >=24}
eslint@10.6.0: eslint@10.7.0:
resolution: {integrity: sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==} resolution: {integrity: sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24} engines: {node: ^20.19.0 || ^22.13.0 || >=24}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@@ -1784,8 +1784,8 @@ packages:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'} engines: {node: '>= 4'}
ignore@7.0.5: ignore@7.0.6:
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==}
engines: {node: '>= 4'} engines: {node: '>= 4'}
imurmurhash@0.1.4: imurmurhash@0.1.4:
@@ -2071,8 +2071,8 @@ packages:
ms@2.1.3: ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
nanoid@3.3.15: nanoid@3.3.16:
resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true hasBin: true
@@ -2126,8 +2126,8 @@ packages:
resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
engines: {node: '>=12'} engines: {node: '>=12'}
postcss@8.5.16: postcss@8.5.19:
resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
powershell-utils@0.1.0: powershell-utils@0.1.0:
@@ -2173,8 +2173,8 @@ packages:
peerDependencies: peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19 react: ^16.8.0 || ^17 || ^18 || ^19
react-i18next@17.0.9: react-i18next@17.0.10:
resolution: {integrity: sha512-buLzOSqHtXxjf+qgSrLWNTXVZ1jSwO6kUv3uJqSP1roGBPgNnbhFm7OmdVwWcgf2gIbUyP0J333uPyx+Btsi3w==} resolution: {integrity: sha512-XneHftyYA774MJkkccSkZ5oKrUpCnXIPmxio3wemqrVzCRLWiGXOMbIzObrer03fNDEnm8g8R5yYls4HcE+esg==}
peerDependencies: peerDependencies:
i18next: '>= 26.2.0' i18next: '>= 26.2.0'
react: '>= 16.8.0' react: '>= 16.8.0'
@@ -2357,8 +2357,8 @@ packages:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
typescript-eslint@8.63.0: typescript-eslint@8.64.0:
resolution: {integrity: sha512-xgwXyzG4sK9ALkBxbyGkTMMOS+imnW65iPhxCQMK83KhxyoDNW7l+IDqEf9vMdoUidHpOoS967RCq4eMiTexwQ==} resolution: {integrity: sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
@@ -2430,8 +2430,8 @@ packages:
vfile@6.0.3: vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
vite@8.1.4: vite@8.1.5:
resolution: {integrity: sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==} resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@@ -2647,9 +2647,9 @@ snapshots:
tslib: 2.8.1 tslib: 2.8.1
optional: true optional: true
'@eslint-community/eslint-utils@4.9.1(eslint@10.6.0(jiti@2.7.0))': '@eslint-community/eslint-utils@4.9.1(eslint@10.7.0(jiti@2.7.0))':
dependencies: dependencies:
eslint: 10.6.0(jiti@2.7.0) eslint: 10.7.0(jiti@2.7.0)
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {} '@eslint-community/regexpp@4.12.2': {}
@@ -2670,9 +2670,9 @@ snapshots:
dependencies: dependencies:
'@types/json-schema': 7.0.15 '@types/json-schema': 7.0.15
'@eslint/js@10.0.1(eslint@10.6.0(jiti@2.7.0))': '@eslint/js@10.0.1(eslint@10.7.0(jiti@2.7.0))':
optionalDependencies: optionalDependencies:
eslint: 10.6.0(jiti@2.7.0) eslint: 10.7.0(jiti@2.7.0)
'@eslint/object-schema@3.0.5': {} '@eslint/object-schema@3.0.5': {}
@@ -3607,17 +3607,17 @@ snapshots:
'@tailwindcss/oxide-win32-arm64-msvc': 4.3.2 '@tailwindcss/oxide-win32-arm64-msvc': 4.3.2
'@tailwindcss/oxide-win32-x64-msvc': 4.3.2 '@tailwindcss/oxide-win32-x64-msvc': 4.3.2
'@tailwindcss/vite@4.3.2(vite@8.1.4(@types/node@26.1.1)(jiti@2.7.0))': '@tailwindcss/vite@4.3.2(vite@8.1.5(@types/node@26.1.1)(jiti@2.7.0))':
dependencies: dependencies:
'@tailwindcss/node': 4.3.2 '@tailwindcss/node': 4.3.2
'@tailwindcss/oxide': 4.3.2 '@tailwindcss/oxide': 4.3.2
tailwindcss: 4.3.2 tailwindcss: 4.3.2
vite: 8.1.4(@types/node@26.1.1)(jiti@2.7.0) vite: 8.1.5(@types/node@26.1.1)(jiti@2.7.0)
'@tanstack/eslint-plugin-query@5.101.2(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': '@tanstack/eslint-plugin-query@5.101.2(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies: dependencies:
'@typescript-eslint/utils': 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/utils': 8.62.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)
eslint: 10.6.0(jiti@2.7.0) eslint: 10.7.0(jiti@2.7.0)
optionalDependencies: optionalDependencies:
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
@@ -3675,30 +3675,30 @@ snapshots:
'@types/unist@3.0.3': {} '@types/unist@3.0.3': {}
'@typescript-eslint/eslint-plugin@8.63.0(@typescript-eslint/parser@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': '@typescript-eslint/eslint-plugin@8.64.0(@typescript-eslint/parser@8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/parser': 8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/scope-manager': 8.63.0 '@typescript-eslint/scope-manager': 8.64.0
'@typescript-eslint/type-utils': 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/type-utils': 8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/utils': 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/utils': 8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.63.0 '@typescript-eslint/visitor-keys': 8.64.0
eslint: 10.6.0(jiti@2.7.0) eslint: 10.7.0(jiti@2.7.0)
ignore: 7.0.5 ignore: 7.0.6
natural-compare: 1.4.0 natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@6.0.3) ts-api-utils: 2.5.0(typescript@6.0.3)
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/parser@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': '@typescript-eslint/parser@8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies: dependencies:
'@typescript-eslint/scope-manager': 8.63.0 '@typescript-eslint/scope-manager': 8.64.0
'@typescript-eslint/types': 8.63.0 '@typescript-eslint/types': 8.64.0
'@typescript-eslint/typescript-estree': 8.63.0(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.64.0(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.63.0 '@typescript-eslint/visitor-keys': 8.64.0
debug: 4.4.3 debug: 4.4.3
eslint: 10.6.0(jiti@2.7.0) eslint: 10.7.0(jiti@2.7.0)
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -3712,10 +3712,10 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/project-service@8.63.0(typescript@6.0.3)': '@typescript-eslint/project-service@8.64.0(typescript@6.0.3)':
dependencies: dependencies:
'@typescript-eslint/tsconfig-utils': 8.63.0(typescript@6.0.3) '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@6.0.3)
'@typescript-eslint/types': 8.63.0 '@typescript-eslint/types': 8.64.0
debug: 4.4.3 debug: 4.4.3
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
@@ -3726,26 +3726,26 @@ snapshots:
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.62.1
'@typescript-eslint/visitor-keys': 8.62.1 '@typescript-eslint/visitor-keys': 8.62.1
'@typescript-eslint/scope-manager@8.63.0': '@typescript-eslint/scope-manager@8.64.0':
dependencies: dependencies:
'@typescript-eslint/types': 8.63.0 '@typescript-eslint/types': 8.64.0
'@typescript-eslint/visitor-keys': 8.63.0 '@typescript-eslint/visitor-keys': 8.64.0
'@typescript-eslint/tsconfig-utils@8.62.1(typescript@6.0.3)': '@typescript-eslint/tsconfig-utils@8.62.1(typescript@6.0.3)':
dependencies: dependencies:
typescript: 6.0.3 typescript: 6.0.3
'@typescript-eslint/tsconfig-utils@8.63.0(typescript@6.0.3)': '@typescript-eslint/tsconfig-utils@8.64.0(typescript@6.0.3)':
dependencies: dependencies:
typescript: 6.0.3 typescript: 6.0.3
'@typescript-eslint/type-utils@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': '@typescript-eslint/type-utils@8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies: dependencies:
'@typescript-eslint/types': 8.63.0 '@typescript-eslint/types': 8.64.0
'@typescript-eslint/typescript-estree': 8.63.0(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.64.0(typescript@6.0.3)
'@typescript-eslint/utils': 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/utils': 8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)
debug: 4.4.3 debug: 4.4.3
eslint: 10.6.0(jiti@2.7.0) eslint: 10.7.0(jiti@2.7.0)
ts-api-utils: 2.5.0(typescript@6.0.3) ts-api-utils: 2.5.0(typescript@6.0.3)
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
@@ -3753,7 +3753,7 @@ snapshots:
'@typescript-eslint/types@8.62.1': {} '@typescript-eslint/types@8.62.1': {}
'@typescript-eslint/types@8.63.0': {} '@typescript-eslint/types@8.64.0': {}
'@typescript-eslint/typescript-estree@8.62.1(typescript@6.0.3)': '@typescript-eslint/typescript-estree@8.62.1(typescript@6.0.3)':
dependencies: dependencies:
@@ -3770,12 +3770,12 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/typescript-estree@8.63.0(typescript@6.0.3)': '@typescript-eslint/typescript-estree@8.64.0(typescript@6.0.3)':
dependencies: dependencies:
'@typescript-eslint/project-service': 8.63.0(typescript@6.0.3) '@typescript-eslint/project-service': 8.64.0(typescript@6.0.3)
'@typescript-eslint/tsconfig-utils': 8.63.0(typescript@6.0.3) '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@6.0.3)
'@typescript-eslint/types': 8.63.0 '@typescript-eslint/types': 8.64.0
'@typescript-eslint/visitor-keys': 8.63.0 '@typescript-eslint/visitor-keys': 8.64.0
debug: 4.4.3 debug: 4.4.3
minimatch: 10.2.5 minimatch: 10.2.5
semver: 7.8.5 semver: 7.8.5
@@ -3785,24 +3785,24 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/utils@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': '@typescript-eslint/utils@8.62.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0)) '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.0))
'@typescript-eslint/scope-manager': 8.62.1 '@typescript-eslint/scope-manager': 8.62.1
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.62.1
'@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3)
eslint: 10.6.0(jiti@2.7.0) eslint: 10.7.0(jiti@2.7.0)
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/utils@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': '@typescript-eslint/utils@8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0)) '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.0))
'@typescript-eslint/scope-manager': 8.63.0 '@typescript-eslint/scope-manager': 8.64.0
'@typescript-eslint/types': 8.63.0 '@typescript-eslint/types': 8.64.0
'@typescript-eslint/typescript-estree': 8.63.0(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.64.0(typescript@6.0.3)
eslint: 10.6.0(jiti@2.7.0) eslint: 10.7.0(jiti@2.7.0)
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -3812,17 +3812,17 @@ snapshots:
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.62.1
eslint-visitor-keys: 5.0.1 eslint-visitor-keys: 5.0.1
'@typescript-eslint/visitor-keys@8.63.0': '@typescript-eslint/visitor-keys@8.64.0':
dependencies: dependencies:
'@typescript-eslint/types': 8.63.0 '@typescript-eslint/types': 8.64.0
eslint-visitor-keys: 5.0.1 eslint-visitor-keys: 5.0.1
'@ungap/structured-clone@1.3.1': {} '@ungap/structured-clone@1.3.1': {}
'@vitejs/plugin-react@6.0.3(vite@8.1.4(@types/node@26.1.1)(jiti@2.7.0))': '@vitejs/plugin-react@6.0.3(vite@8.1.5(@types/node@26.1.1)(jiti@2.7.0))':
dependencies: dependencies:
'@rolldown/pluginutils': 1.0.1 '@rolldown/pluginutils': 1.0.1
vite: 8.1.4(@types/node@26.1.1)(jiti@2.7.0) vite: 8.1.5(@types/node@26.1.1)(jiti@2.7.0)
acorn-jsx@5.3.2(acorn@8.17.0): acorn-jsx@5.3.2(acorn@8.17.0):
dependencies: dependencies:
@@ -3997,20 +3997,20 @@ snapshots:
escape-string-regexp@4.0.0: {} escape-string-regexp@4.0.0: {}
eslint-plugin-react-hooks@7.1.1(eslint@10.6.0(jiti@2.7.0)): eslint-plugin-react-hooks@7.1.1(eslint@10.7.0(jiti@2.7.0)):
dependencies: dependencies:
'@babel/core': 7.29.0 '@babel/core': 7.29.0
'@babel/parser': 7.29.3 '@babel/parser': 7.29.3
eslint: 10.6.0(jiti@2.7.0) eslint: 10.7.0(jiti@2.7.0)
hermes-parser: 0.25.1 hermes-parser: 0.25.1
zod: 4.4.3 zod: 4.4.3
zod-validation-error: 4.0.2(zod@4.4.3) zod-validation-error: 4.0.2(zod@4.4.3)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-plugin-react-refresh@0.5.3(eslint@10.6.0(jiti@2.7.0)): eslint-plugin-react-refresh@0.5.3(eslint@10.7.0(jiti@2.7.0)):
dependencies: dependencies:
eslint: 10.6.0(jiti@2.7.0) eslint: 10.7.0(jiti@2.7.0)
eslint-scope@9.1.2: eslint-scope@9.1.2:
dependencies: dependencies:
@@ -4023,9 +4023,9 @@ snapshots:
eslint-visitor-keys@5.0.1: {} eslint-visitor-keys@5.0.1: {}
eslint@10.6.0(jiti@2.7.0): eslint@10.7.0(jiti@2.7.0):
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0)) '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.0))
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.23.5 '@eslint/config-array': 0.23.5
'@eslint/config-helpers': 0.6.0 '@eslint/config-helpers': 0.6.0
@@ -4226,7 +4226,7 @@ snapshots:
ignore@5.3.2: {} ignore@5.3.2: {}
ignore@7.0.5: {} ignore@7.0.6: {}
imurmurhash@0.1.4: {} imurmurhash@0.1.4: {}
@@ -4591,7 +4591,7 @@ snapshots:
ms@2.1.3: {} ms@2.1.3: {}
nanoid@3.3.15: {} nanoid@3.3.16: {}
natural-compare@1.4.0: {} natural-compare@1.4.0: {}
@@ -4648,9 +4648,9 @@ snapshots:
picomatch@4.0.5: {} picomatch@4.0.5: {}
postcss@8.5.16: postcss@8.5.19:
dependencies: dependencies:
nanoid: 3.3.15 nanoid: 3.3.16
picocolors: 1.1.1 picocolors: 1.1.1
source-map-js: 1.2.1 source-map-js: 1.2.1
@@ -4736,7 +4736,7 @@ snapshots:
dependencies: dependencies:
react: 19.2.7 react: 19.2.7
react-i18next@17.0.9(i18next@26.3.6(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3): react-i18next@17.0.10(i18next@26.3.6(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3):
dependencies: dependencies:
'@babel/runtime': 7.29.7 '@babel/runtime': 7.29.7
html-parse-stringify: 3.0.1 html-parse-stringify: 3.0.1
@@ -4923,13 +4923,13 @@ snapshots:
dependencies: dependencies:
prelude-ls: 1.2.1 prelude-ls: 1.2.1
typescript-eslint@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3): typescript-eslint@8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3):
dependencies: dependencies:
'@typescript-eslint/eslint-plugin': 8.63.0(@typescript-eslint/parser@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/eslint-plugin': 8.64.0(@typescript-eslint/parser@8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/parser': 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/parser': 8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/typescript-estree': 8.63.0(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.64.0(typescript@6.0.3)
'@typescript-eslint/utils': 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/utils': 8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)
eslint: 10.6.0(jiti@2.7.0) eslint: 10.7.0(jiti@2.7.0)
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -5010,11 +5010,11 @@ snapshots:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
vfile-message: 4.0.3 vfile-message: 4.0.3
vite@8.1.4(@types/node@26.1.1)(jiti@2.7.0): vite@8.1.5(@types/node@26.1.1)(jiti@2.7.0):
dependencies: dependencies:
lightningcss: 1.32.0 lightningcss: 1.32.0
picomatch: 4.0.5 picomatch: 4.0.5
postcss: 8.5.16 postcss: 8.5.19
rolldown: 1.1.5 rolldown: 1.1.5
tinyglobby: 0.2.17 tinyglobby: 0.2.17
optionalDependencies: optionalDependencies:
@@ -32,8 +32,8 @@ import { useTranslation } from "react-i18next";
import { useLocation } from "react-router"; import { useLocation } from "react-router";
import { useRef } from "react"; import { useRef } from "react";
import { import {
searchParamsFromObject,
useScreenParams, useScreenParams,
recompileScreenParams,
} from "@/lib/hooks/screen-params"; } from "@/lib/hooks/screen-params";
import { useMutation } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query";
import axios from "axios"; import axios from "axios";
@@ -70,7 +70,13 @@ export const QuickActions = () => {
const redirectTimer = useRef<number | null>(null); const redirectTimer = useRef<number | null>(null);
const searchParams = new URLSearchParams(search); const searchParams = new URLSearchParams(search);
const screenParams = useScreenParams(searchParams); const screenParams = useScreenParams(searchParams);
const compiledParams = recompileScreenParams(screenParams); const compiledParams = (() => {
const params = searchParamsFromObject(screenParams).toString();
if (params.length > 0) {
return `?${params}`;
}
return "";
})();
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
+6 -2
View File
@@ -1,10 +1,11 @@
type UseLoginForProps = { type UseLoginForProps = {
login_for?: "oidc" | "app"; login_for?: "oidc" | "app";
compiledParams: string; params: URLSearchParams;
}; };
export const useLoginFor = (props: UseLoginForProps): string => { export const useLoginFor = (props: UseLoginForProps): string => {
const { login_for, compiledParams } = props; const { login_for, params } = props;
const compiledParams = params.toString() ? "?" + params.toString() : "";
switch (login_for) { switch (login_for) {
case "oidc": case "oidc":
@@ -12,6 +13,9 @@ export const useLoginFor = (props: UseLoginForProps): string => {
case "app": case "app":
return "/continue" + compiledParams; return "/continue" + compiledParams;
default: default:
if (params.get("redirect_uri")) {
return "/continue" + compiledParams
}
return "/logout"; return "/logout";
} }
}; };
+20 -7
View File
@@ -88,13 +88,29 @@ const getEffectivePort = (url: URL): string => {
return "80"; return "80";
}; };
// https://www.geeksforgeeks.org/javascript/how-to-check-if-a-string-is-a-valid-ip-address-format-in-javascript
const isIP = (str: string): boolean => {
const ipv4 =
/^(\d{1,3}\.){3}\d{1,3}$/;
const ipv6 =
/^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
return ipv4.test(str) || ipv6.test(str) || str.startsWith("[");
}
const trimPeriod = (str: string): string => {
if(str.lastIndexOf('.') === (str.length - 1)){
str = str.substring(0, str.length - 1);
}
return str
}
export const isTrustedDomain = ( export const isTrustedDomain = (
url: URL, url: URL,
appUrl: URL, appUrl: URL,
cookieDomain: string, cookieDomain: string,
subdomainsEnabled: boolean, subdomainsEnabled: boolean,
): boolean => { ): boolean => {
if (url.protocol != appUrl.protocol) { if (isIP(url.hostname)) {
return false; return false;
} }
@@ -102,7 +118,7 @@ export const isTrustedDomain = (
return false; return false;
} }
if (url.hostname == appUrl.hostname) { if (trimPeriod(url.hostname) == trimPeriod(appUrl.hostname)) {
return true; return true;
} }
@@ -110,9 +126,6 @@ export const isTrustedDomain = (
return false; return false;
} }
if (url.hostname.endsWith("." + cookieDomain.toLowerCase())) { return trimPeriod(url.hostname).endsWith("." + cookieDomain.toLowerCase())
return true; || trimPeriod(url.hostname) == cookieDomain.toLowerCase();
}
return false;
}; };
+4 -10
View File
@@ -27,16 +27,10 @@ export function useScreenParams(params: URLSearchParams): ScreenParams {
return parsed.data; return parsed.data;
} }
export function recompileScreenParams(params: ScreenParams): string { export function searchParamsFromObject(obj: object): URLSearchParams {
const p = new URLSearchParams( return new URLSearchParams(
Object.fromEntries( Object.fromEntries(
Object.entries(params).filter(([, v]) => v !== undefined), Object.entries(obj).filter(([, v]) => v !== undefined),
) as Record<string, string>, ) as Record<string, string>,
).toString(); );
if (p.length > 0) {
return "?" + p;
}
return "";
} }
+8 -2
View File
@@ -22,7 +22,7 @@ import {
TooltipTrigger, TooltipTrigger,
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { import {
recompileScreenParams, searchParamsFromObject,
useScreenParams, useScreenParams,
} from "@/lib/hooks/screen-params"; } from "@/lib/hooks/screen-params";
import { useEffect } from "react"; import { useEffect } from "react";
@@ -89,7 +89,13 @@ export const AuthorizePage = () => {
const searchParams = new URLSearchParams(search); const searchParams = new URLSearchParams(search);
const screenParams = useScreenParams(searchParams); const screenParams = useScreenParams(searchParams);
const isOidc = screenParams.login_for === "oidc"; const isOidc = screenParams.login_for === "oidc";
const compiledParams = recompileScreenParams(screenParams); const compiledParams = (() => {
const params = searchParamsFromObject(screenParams).toString();
if (params.length > 0) {
return `?${params}`;
}
return "";
})();
// TODO: maybe a better way to do this // TODO: maybe a better way to do this
const shouldAutoAuthorize = const shouldAutoAuthorize =
+9 -3
View File
@@ -13,7 +13,7 @@ import { Navigate, useLocation, useNavigate } from "react-router";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { useRedirectUri } from "@/lib/hooks/redirect-uri"; import { useRedirectUri } from "@/lib/hooks/redirect-uri";
import { import {
recompileScreenParams, searchParamsFromObject,
useScreenParams, useScreenParams,
} from "@/lib/hooks/screen-params"; } from "@/lib/hooks/screen-params";
@@ -32,7 +32,13 @@ export const ContinuePage = () => {
const screenParams = useScreenParams(searchParams); const screenParams = useScreenParams(searchParams);
const redirectUri = screenParams.redirect_uri; const redirectUri = screenParams.redirect_uri;
const isAppLogin = screenParams.login_for === "app"; const isAppLogin = screenParams.login_for === "app";
const recompiledParams = recompileScreenParams(screenParams); const compiledParams = (() => {
const params = searchParamsFromObject(screenParams).toString();
if (params.length > 0) {
return `?${params}`;
}
return "";
})();
const { url, valid, trusted, allowedProto, httpsDowngrade } = useRedirectUri( const { url, valid, trusted, allowedProto, httpsDowngrade } = useRedirectUri(
redirectUri, redirectUri,
@@ -89,7 +95,7 @@ export const ContinuePage = () => {
}, [shouldAutoRedirect, redirectToTarget]); }, [shouldAutoRedirect, redirectToTarget]);
if (!auth.authenticated) { if (!auth.authenticated) {
return <Navigate to={`/login${recompiledParams}`} replace />; return <Navigate to={`/login${compiledParams}`} replace />;
} }
if (!hasValidRedirect || !isAppLogin) { if (!hasValidRedirect || !isAppLogin) {
+9 -3
View File
@@ -12,8 +12,8 @@ import { useTranslation } from "react-i18next";
import Markdown from "react-markdown"; import Markdown from "react-markdown";
import { useLocation } from "react-router"; import { useLocation } from "react-router";
import { import {
recompileScreenParams, searchParamsFromObject,
useScreenParams, useScreenParams,
} from "@/lib/hooks/screen-params"; } from "@/lib/hooks/screen-params";
export const ForgotPasswordPage = () => { export const ForgotPasswordPage = () => {
@@ -22,7 +22,13 @@ export const ForgotPasswordPage = () => {
const { search } = useLocation(); const { search } = useLocation();
const searchParams = new URLSearchParams(search); const searchParams = new URLSearchParams(search);
const screenParams = useScreenParams(searchParams); const screenParams = useScreenParams(searchParams);
const compiledParams = recompileScreenParams(screenParams); const compiledParams = (() => {
const params = searchParamsFromObject(screenParams).toString();
if (params.length > 0) {
return `?${params}`;
}
return "";
})();
return ( return (
<Card> <Card>
+9 -6
View File
@@ -26,7 +26,7 @@ import { useTranslation } from "react-i18next";
import { Navigate, useLocation } from "react-router"; import { Navigate, useLocation } from "react-router";
import { toast } from "sonner"; import { toast } from "sonner";
import { import {
recompileScreenParams, searchParamsFromObject,
useScreenParams, useScreenParams,
} from "@/lib/hooks/screen-params"; } from "@/lib/hooks/screen-params";
import { useLoginFor } from "@/lib/hooks/login-for"; import { useLoginFor } from "@/lib/hooks/login-for";
@@ -63,13 +63,16 @@ export const LoginPage = () => {
const searchParams = new URLSearchParams(search); const searchParams = new URLSearchParams(search);
const screenParams = useScreenParams(searchParams); const screenParams = useScreenParams(searchParams);
const compiledParams = recompileScreenParams({ const compiledParams = (() => {
...screenParams, const params = searchParamsFromObject(screenParams).toString();
oidc_prompt: undefined, if (params.length > 0) {
}); return `?${params}`;
}
return "";
})();
const loginForUrl = useLoginFor({ const loginForUrl = useLoginFor({
login_for: screenParams.login_for, login_for: screenParams.login_for,
compiledParams, params: searchParamsFromObject({ ...screenParams, oidc_prompt: undefined}),
}); });
const [isOauthAutoRedirect, setIsOauthAutoRedirect] = useState( const [isOauthAutoRedirect, setIsOauthAutoRedirect] = useState(
+8 -2
View File
@@ -17,8 +17,8 @@ import { type UseMutationResult } from "@tanstack/react-query";
import { type AxiosResponse } from "axios"; import { type AxiosResponse } from "axios";
import { useLocation } from "react-router"; import { useLocation } from "react-router";
import { import {
searchParamsFromObject,
useScreenParams, useScreenParams,
recompileScreenParams,
} from "@/lib/hooks/screen-params"; } from "@/lib/hooks/screen-params";
export const LogoutPage = () => { export const LogoutPage = () => {
@@ -29,7 +29,13 @@ export const LogoutPage = () => {
const redirectTimer = useRef<number | null>(null); const redirectTimer = useRef<number | null>(null);
const searchParams = new URLSearchParams(search); const searchParams = new URLSearchParams(search);
const screenParams = useScreenParams(searchParams); const screenParams = useScreenParams(searchParams);
const compiledParams = recompileScreenParams(screenParams); const compiledParams = (() => {
const params = searchParamsFromObject(screenParams).toString();
if (params.length > 0) {
return `?${params}`;
}
return "";
})();
const logoutMutation = useMutation({ const logoutMutation = useMutation({
mutationFn: () => axios.post("/api/user/logout"), mutationFn: () => axios.post("/api/user/logout"),
+9 -3
View File
@@ -17,7 +17,7 @@ import { useTranslation } from "react-i18next";
import { Navigate, useLocation } from "react-router"; import { Navigate, useLocation } from "react-router";
import { toast } from "sonner"; import { toast } from "sonner";
import { import {
recompileScreenParams, searchParamsFromObject,
useScreenParams, useScreenParams,
} from "@/lib/hooks/screen-params"; } from "@/lib/hooks/screen-params";
import { useLoginFor } from "@/lib/hooks/login-for"; import { useLoginFor } from "@/lib/hooks/login-for";
@@ -32,10 +32,16 @@ export const TotpPage = () => {
const searchParams = new URLSearchParams(search); const searchParams = new URLSearchParams(search);
const screenParams = useScreenParams(searchParams); const screenParams = useScreenParams(searchParams);
const compiledParams = recompileScreenParams(screenParams); const compiledParams = (() => {
const params = searchParamsFromObject(screenParams).toString();
if (params.length > 0) {
return `?${params}`;
}
return "";
})();
const loginForUrl = useLoginFor({ const loginForUrl = useLoginFor({
login_for: screenParams.login_for, login_for: screenParams.login_for,
compiledParams, params: searchParamsFromObject(screenParams),
}); });
const totpMutation = useMutation({ const totpMutation = useMutation({
+5 -5
View File
@@ -9,7 +9,7 @@ require (
github.com/docker/docker v28.5.2+incompatible github.com/docker/docker v28.5.2+incompatible
github.com/gin-gonic/gin v1.12.0 github.com/gin-gonic/gin v1.12.0
github.com/go-jose/go-jose/v4 v4.1.4 github.com/go-jose/go-jose/v4 v4.1.4
github.com/go-ldap/ldap/v3 v3.4.13 github.com/go-ldap/ldap/v3 v3.4.14
github.com/golang-jwt/jwt/v5 v5.3.1 github.com/golang-jwt/jwt/v5 v5.3.1
github.com/golang-migrate/migrate/v4 v4.19.1 github.com/golang-migrate/migrate/v4 v4.19.1
github.com/google/go-querystring v1.2.0 github.com/google/go-querystring v1.2.0
@@ -24,12 +24,13 @@ require (
github.com/weppos/publicsuffix-go v0.50.3 github.com/weppos/publicsuffix-go v0.50.3
go.uber.org/dig v1.19.0 go.uber.org/dig v1.19.0
golang.org/x/crypto v0.54.0 golang.org/x/crypto v0.54.0
golang.org/x/net v0.57.0
golang.org/x/oauth2 v0.36.0 golang.org/x/oauth2 v0.36.0
golang.org/x/tools v0.48.0 golang.org/x/tools v0.48.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.36.2 k8s.io/apimachinery v0.36.2
k8s.io/client-go v0.36.2 k8s.io/client-go v0.36.2
modernc.org/sqlite v1.53.0 modernc.org/sqlite v1.54.0
) )
require ( require (
@@ -72,7 +73,7 @@ require (
github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8 // indirect
github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonreference v0.20.4 // indirect github.com/go-openapi/jsonreference v0.20.4 // indirect
@@ -135,7 +136,6 @@ require (
golang.org/x/arch v0.22.0 // indirect golang.org/x/arch v0.22.0 // indirect
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
golang.org/x/mod v0.38.0 // indirect golang.org/x/mod v0.38.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/sync v0.22.0 // indirect golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect golang.org/x/sys v0.47.0 // indirect
golang.org/x/term v0.45.0 // indirect golang.org/x/term v0.45.0 // indirect
@@ -147,7 +147,7 @@ require (
k8s.io/klog/v2 v2.140.0 // indirect k8s.io/klog/v2 v2.140.0 // indirect
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
modernc.org/libc v1.73.4 // indirect modernc.org/libc v1.74.1 // indirect
modernc.org/mathutil v1.7.1 // indirect modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect modernc.org/memory v1.11.0 // indirect
rsc.io/qr v0.2.0 // indirect rsc.io/qr v0.2.0 // indirect
+14 -14
View File
@@ -113,12 +113,12 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-asn1-ber/asn1-ber v1.5.8/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-ldap/ldap/v3 v3.4.13 h1:+x1nG9h+MZN7h/lUi5Q3UZ0fJ1GyDQYbPvbuH38baDQ= github.com/go-ldap/ldap/v3 v3.4.14 h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs=
github.com/go-ldap/ldap/v3 v3.4.13/go.mod h1:LxsGZV6vbaK0sIvYfsv47rfh4ca0JXokCoKjZxsszv0= github.com/go-ldap/ldap/v3 v3.4.14/go.mod h1:S4eJUMUNjDkE0ZJtIZdybwyb03sGGLW6gxXT1Hs8VKA=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -392,20 +392,20 @@ k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hk
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
modernc.org/cc/v4 v4.28.4 h1:Hd/4Es+MBj+/7hSdZaisNyu6bv3V0Dp2MdllyfqaH+c= modernc.org/cc/v4 v4.29.0 h1:CXgwL8cvxmyzBQZzbSl/6xFtMCryb6u8IOqDci39cgc=
modernc.org/cc/v4 v4.28.4/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= modernc.org/cc/v4 v4.29.0/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
modernc.org/ccgo/v4 v4.34.4 h1:OVnSOWQjVKOYkFxoHYB+qQmSHK5gqMqARM+K9DpR/Ws= modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU=
modernc.org/ccgo/v4 v4.34.4/go.mod h1:qdKqE8FNIYyysougB1RX9MxCzp5oJOcQXSobANJ4TuE= modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk=
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/gc/v3 v3.1.3 h1:6QAplYyVO+KdPW3pGnqmJDUxtkec8ooEWvks/hhU3lc= modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI=
modernc.org/gc/v3 v3.1.3/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.73.4 h1:+ra4Ui8ngyt8HDcO1FTDPWlkAh6yOdaO2yAoh8MddQA= modernc.org/libc v1.74.1 h1:bdR4VTKFMC4966QSNZ05XLGI/VwzVa2kTUX51Dm0riQ=
modernc.org/libc v1.73.4/go.mod h1:DXZ3eO8qMCNn2SnmTNCiC71nJ9Rcq3PsnpU6Vc4rWK8= modernc.org/libc v1.74.1/go.mod h1:uH4t5bOx3G3g9Xcmj10YKlTcVISlRDwv8VoQJG9n8Os=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
@@ -414,8 +414,8 @@ modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.53.0 h1:20WG8N9q4ji/dEqGk4uiI0c6OPjSeLTNYGFCc3+7c1M= modernc.org/sqlite v1.54.0 h1:JCxR4qwkJvOaqAoYcgDoO25Nc+ROg6EJ2LfBVzdrgog=
modernc.org/sqlite v1.53.0/go.mod h1:xoEpOIpGrgT48H5iiyt/YXPCZPEzlfmfFwtk8Lklw8s= modernc.org/sqlite v1.54.0/go.mod h1:4ntCLuNmnH8+GNqjka1wNg7KJd5/Hi5FYp8K+XQ7GZw=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
+9 -7
View File
@@ -88,17 +88,13 @@ func (app *BootstrapApp) Setup() error {
app.log.App.Info().Msgf("Starting Tinyauth version: %s", model.Version) app.log.App.Info().Msgf("Starting Tinyauth version: %s", model.Version)
// get app url // get app url
if app.config.AppURL == "" { appURL, err := utils.SafeParseAppURL(app.config.AppURL)
return errors.New("app url cannot be empty, perhaps config loading failed")
}
appUrl, err := url.Parse(app.config.AppURL)
if err != nil { if err != nil {
return fmt.Errorf("failed to parse app url: %w", err) return fmt.Errorf("failed to parse app url: %w", err)
} }
app.runtime.AppURL = strings.ToLower(appUrl.Scheme + "://" + appUrl.Host) app.runtime.AppURL = appURL
// validate session config // validate session config
if app.config.Auth.SessionMaxLifetime != 0 && app.config.Auth.SessionMaxLifetime < app.config.Auth.SessionExpiry { if app.config.Auth.SessionMaxLifetime != 0 && app.config.Auth.SessionMaxLifetime < app.config.Auth.SessionExpiry {
@@ -172,7 +168,13 @@ func (app *BootstrapApp) Setup() error {
app.runtime.CookieDomain = cookieDomain app.runtime.CookieDomain = cookieDomain
// cookie names // cookie names
app.runtime.UUID = utils.GenerateUUID(appUrl.Hostname()) u, err := url.Parse(app.runtime.AppURL)
if err != nil {
return fmt.Errorf("failed to parse app url: %w", err)
}
app.runtime.UUID = utils.GenerateUUID(u.Hostname())
cookieId := strings.Split(app.runtime.UUID, "-")[0] // first 8 characters of the uuid should be good enough cookieId := strings.Split(app.runtime.UUID, "-")[0] // first 8 characters of the uuid should be good enough
+3 -4
View File
@@ -313,8 +313,7 @@ func (controller *OAuthController) getCookieDomain() string {
func (controller *OAuthController) isRedirectSafe(redirectURI string) bool { func (controller *OAuthController) isRedirectSafe(redirectURI string) bool {
v := validators.NewDomainValidator(validators.DomainValidatorOptions{ v := validators.NewDomainValidator(validators.DomainValidatorOptions{
WithScheme: true, WithPort: true,
WithPort: true,
}) })
_, err := v.SafeHostname(controller.runtime.AppURL) _, err := v.SafeHostname(controller.runtime.AppURL)
@@ -333,7 +332,6 @@ func (controller *OAuthController) isRedirectSafe(redirectURI string) bool {
controller.log.App.Debug().Err(err).Msg("Failed to validate redirect URI") controller.log.App.Debug().Err(err).Msg("Failed to validate redirect URI")
if errors.Is(err, validators.ErrInvalidURL) || if errors.Is(err, validators.ErrInvalidURL) ||
errors.Is(err, validators.ErrSchemeMismatch) ||
errors.Is(err, validators.ErrPortMismatch) { errors.Is(err, validators.ErrPortMismatch) {
return false return false
} }
@@ -351,7 +349,8 @@ func (controller *OAuthController) isRedirectSafe(redirectURI string) bool {
return false return false
} }
if strings.HasSuffix(hostname, "."+strings.ToLower(controller.runtime.CookieDomain)) { if strings.HasSuffix(hostname, "."+strings.ToLower(controller.runtime.CookieDomain)) ||
hostname == controller.runtime.CookieDomain {
return true return true
} }
@@ -81,14 +81,6 @@ func TestOAuthController_isRedirectSafe(t *testing.T) {
redirectURI: "https://sub.example.com", redirectURI: "https://sub.example.com",
expected: false, expected: false,
}, },
{
description: "Cookie domain itself is not a subdomain match",
appURL: "https://tinyauth.example.com",
cookieDomain: "example.com",
subdomainsEnabled: true,
redirectURI: "https://example.com",
expected: false,
},
{ {
description: "Different scheme returns false", description: "Different scheme returns false",
appURL: "https://tinyauth.example.com", appURL: "https://tinyauth.example.com",
@@ -121,14 +113,6 @@ func TestOAuthController_isRedirectSafe(t *testing.T) {
redirectURI: "https:/malicious", redirectURI: "https:/malicious",
expected: false, expected: false,
}, },
{
description: "Redirect URI without scheme returns false",
appURL: "https://tinyauth.example.com",
cookieDomain: "example.com",
subdomainsEnabled: true,
redirectURI: "tinyauth.example.com",
expected: false,
},
{ {
description: "Relative redirect URI returns false", description: "Relative redirect URI returns false",
appURL: "https://tinyauth.example.com", appURL: "https://tinyauth.example.com",
+11 -7
View File
@@ -44,13 +44,17 @@ func (service *AccessControlsService) lookupStaticACLs(domain string) *model.App
// First try to find a matching app by domain, then fallback to matching by app name (subdomain) // First try to find a matching app by domain, then fallback to matching by app name (subdomain)
for app, config := range service.config.Apps { for app, config := range service.config.Apps {
err := v.Validate(config.Config.Domain, domain) if config.Config.Domain != "" {
if err == nil { err := v.Validate(config.Config.Domain, domain)
service.log.App.Debug().Str("name", app).Msg("Found matching container by domain") if err == nil {
return &config service.log.App.Debug().Str("name", app).Msg("Found matching container by domain")
} return &config
if !errors.Is(err, validators.ErrHostnameMismatch) { }
service.log.App.Debug().Str("name", app).Err(err).Msg("Domain validation failed") if !errors.Is(err, validators.ErrHostnameMismatch) &&
!errors.Is(err, validators.ErrPortMismatch) &&
!errors.Is(err, validators.ErrSchemeMismatch) {
service.log.App.Debug().Str("name", app).Err(err).Msg("Domain validation failed")
}
} }
if strings.HasPrefix(strings.ToLower(domain), strings.ToLower(app+".")) { if strings.HasPrefix(strings.ToLower(domain), strings.ToLower(app+".")) {
service.log.App.Debug().Str("name", app).Msg("Found matching container by app name") service.log.App.Debug().Str("name", app).Msg("Found matching container by app name")
+46 -5
View File
@@ -7,10 +7,55 @@ import (
"strings" "strings"
"github.com/weppos/publicsuffix-go/publicsuffix" "github.com/weppos/publicsuffix-go/publicsuffix"
"golang.org/x/net/idna"
) )
// GetCookieDomain parses the app url and returns the domain value to use for cookies. var (
ErrEmptyURL = fmt.Errorf("invalid url")
)
func SafeParseAppURL(str string) (string, error) {
if strings.TrimSpace(str) == "" {
return "", ErrEmptyURL
}
u, err := url.Parse(str)
if err != nil {
return "", fmt.Errorf("invalid url: %w", err)
}
if u.Host == "" ||
(u.Scheme != "http" &&
u.Scheme != "https") {
return "", fmt.Errorf("invalid url, must be in format https(s)://host")
}
hostname := strings.ToLower(u.Hostname())
hostname = strings.TrimSuffix(hostname, ".")
if netIP := net.ParseIP(hostname); netIP != nil {
return "", fmt.Errorf("ip addresses not allowed")
}
hostname, err = idna.Lookup.ToASCII(hostname)
if err != nil {
return "", fmt.Errorf("failed to convert hostname to ascii: %w", err)
}
appURL := fmt.Sprintf("%s://%s", u.Scheme, hostname)
if u.Port() != "" {
appURL += ":" + u.Port()
}
return appURL, nil
}
// GetCookieDomain parses the app URL and returns the domain value to use for cookies.
// When auth for subdomains is enabled, it strips the leftmost label // When auth for subdomains is enabled, it strips the leftmost label
// GetCookieDomain assumes the app URL is first parsed with SafeParseAppURL
// (e.g. sub1.sub2.domain.com -> sub2.domain.com), otherwise it returns the full hostname. // (e.g. sub1.sub2.domain.com -> sub2.domain.com), otherwise it returns the full hostname.
func GetCookieDomain(appUrl string, subdomainsEnabled bool) (string, error) { func GetCookieDomain(appUrl string, subdomainsEnabled bool) (string, error) {
u, err := url.Parse(appUrl) u, err := url.Parse(appUrl)
@@ -21,10 +66,6 @@ func GetCookieDomain(appUrl string, subdomainsEnabled bool) (string, error) {
hostname := strings.ToLower(u.Hostname()) hostname := strings.ToLower(u.Hostname())
if netIP := net.ParseIP(hostname); netIP != nil {
return "", fmt.Errorf("ip addresses not allowed")
}
parts := strings.Split(hostname, ".") parts := strings.Split(hostname, ".")
if len(parts) < 2 { if len(parts) < 2 {
+85 -6
View File
@@ -7,7 +7,86 @@ import (
"github.com/tinyauthapp/tinyauth/internal/utils" "github.com/tinyauthapp/tinyauth/internal/utils"
) )
func TestGetRootDomain(t *testing.T) { func TestSafeParseAPPURL(t *testing.T) {
// Normal app url
appURL := "http://sub.tinyauth.app"
expected := "http://sub.tinyauth.app"
result, err := utils.SafeParseAppURL(appURL)
assert.NoError(t, err)
assert.Equal(t, expected, result)
// Strip path
appURL = "http://sub.tinyauth.app/path"
expected = "http://sub.tinyauth.app"
result, err = utils.SafeParseAppURL(appURL)
assert.NoError(t, err)
assert.Equal(t, expected, result)
// Preserve port
appURL = "http://sub.tinyauth.app:8080"
expected = "http://sub.tinyauth.app:8080"
result, err = utils.SafeParseAppURL(appURL)
assert.NoError(t, err)
assert.Equal(t, expected, result)
// Remove trailing dot
appURL = "http://sub.tinyauth.app."
expected = "http://sub.tinyauth.app"
result, err = utils.SafeParseAppURL(appURL)
assert.NoError(t, err)
assert.Equal(t, expected, result)
// Convert to ascii
appURL = "http://bücher.example.com"
expected = "http://xn--bcher-kva.example.com"
result, err = utils.SafeParseAppURL(appURL)
assert.NoError(t, err)
assert.Equal(t, expected, result)
// Lowercase
appURL = "HTTP://SUb.tinyAUth.aPP"
expected = "http://sub.tinyauth.app"
result, err = utils.SafeParseAppURL(appURL)
assert.NoError(t, err)
assert.Equal(t, expected, result)
// Empty string
appURL = ""
_, err = utils.SafeParseAppURL(appURL)
assert.ErrorIs(t, err, utils.ErrEmptyURL)
// Invalid URL
appURL = "invalidurl"
_, err = utils.SafeParseAppURL(appURL)
assert.ErrorContains(t, err, "invalid url")
// Non http or https URL
appURL = "ftp://sub.tinyauth.app"
_, err = utils.SafeParseAppURL(appURL)
assert.ErrorContains(t, err, "invalid url")
// Invalid punycode
appURL = "http://ab--cd.example.com"
_, err = utils.SafeParseAppURL(appURL)
assert.ErrorContains(t, err, "failed to convert hostname to ascii")
// IP address
appURL = "http://10.10.10.10"
_, err = utils.SafeParseAppURL(appURL)
assert.ErrorContains(t, err, "ip addresses not allowed")
// IPv6 address
appURL = "http://[::1]:8080"
_, err = utils.SafeParseAppURL(appURL)
assert.ErrorContains(t, err, "ip addresses not allowed")
// Invalid URL
appURL = "://"
_, err = utils.SafeParseAppURL(appURL)
assert.ErrorContains(t, err, "invalid url")
}
func TestGetCookieDomain(t *testing.T) {
// Normal case // Normal case
domain := "http://sub.tinyauth.app" domain := "http://sub.tinyauth.app"
expected := "tinyauth.app" expected := "tinyauth.app"
@@ -27,11 +106,6 @@ func TestGetRootDomain(t *testing.T) {
_, err = utils.GetCookieDomain(domain, true) _, err = utils.GetCookieDomain(domain, true)
assert.EqualError(t, err, "invalid app url, must be in format subdomain.domain.tld or domain.tld") assert.EqualError(t, err, "invalid app url, must be in format subdomain.domain.tld or domain.tld")
// IP address
domain = "http://10.10.10.10"
_, err = utils.GetCookieDomain(domain, true)
assert.ErrorContains(t, err, "ip addresses not allowed")
// Invalid URL // Invalid URL
domain = "http://[::1]:namedport" domain = "http://[::1]:namedport"
_, err = utils.GetCookieDomain(domain, true) _, err = utils.GetCookieDomain(domain, true)
@@ -56,6 +130,11 @@ func TestGetRootDomain(t *testing.T) {
_, err = utils.GetCookieDomain(domain, true) _, err = utils.GetCookieDomain(domain, true)
assert.ErrorContains(t, err, "domain in public suffix list, cannot set cookies") assert.ErrorContains(t, err, "domain in public suffix list, cannot set cookies")
// Domain managed by ICANN without subdomain
domain = "http://co.uk"
_, err = utils.GetCookieDomain(domain, true)
assert.ErrorContains(t, err, "domain in public suffix list, cannot set cookies")
// Domain without subdomain // Domain without subdomain
domain = "http://tinyauth.app" domain = "http://tinyauth.app"
expected = "tinyauth.app" expected = "tinyauth.app"
+1 -1
View File
@@ -63,7 +63,7 @@ func (v *DomainValidator) getURL(i string) (*url.URL, error) {
return nil, ErrInvalidURL return nil, ErrInvalidURL
} }
if v.opts.WithPort && !v.opts.WithScheme && u.Port() == "" { if v.opts.WithPort && u.Port() == "" && (u.Scheme != "http" && u.Scheme != "https") {
return nil, fmt.Errorf("port validation is enabled but port is missing in input url and schemes are not enabled") return nil, fmt.Errorf("port validation is enabled but port is missing in input url and schemes are not enabled")
} }
+32 -2
View File
@@ -104,6 +104,24 @@ func TestDomainValidator_SafeHostname(t *testing.T) {
assert.ErrorContains(t, e, "input url is missing scheme") assert.ErrorContains(t, e, "input url is missing scheme")
}, },
}, },
{
description: "With port enabled but without scheme and url with https should work",
options: DomainValidatorOptions{WithPort: true},
input: "https://example.com",
expected: "example.com",
},
{
description: "With port enabled but without scheme and url with http should work",
options: DomainValidatorOptions{WithPort: true},
input: "http://example.com",
expected: "example.com",
},
{
description: "With port enabled but without scheme and url with port should work",
options: DomainValidatorOptions{WithPort: true},
input: "example.com:8080",
expected: "example.com",
},
} }
for _, test := range tests { for _, test := range tests {
@@ -194,7 +212,7 @@ func TestDomainValidator_Validate(t *testing.T) {
expected: "ssh://example.com:22", expected: "ssh://example.com:22",
actual: "ssh://example.com", actual: "ssh://example.com",
errorFunc: func(t *testing.T, e error) { errorFunc: func(t *testing.T, e error) {
assert.ErrorContains(t, e, "failed to get effective port for url") assert.ErrorContains(t, e, "port validation is enabled but port is missing in input url and schemes are not enabled")
}, },
}, },
{ {
@@ -203,9 +221,21 @@ func TestDomainValidator_Validate(t *testing.T) {
expected: "ssh://example.com", expected: "ssh://example.com",
actual: "ssh://example.com:22", actual: "ssh://example.com:22",
errorFunc: func(t *testing.T, e error) { errorFunc: func(t *testing.T, e error) {
assert.ErrorContains(t, e, "failed to get effective port for url") assert.ErrorContains(t, e, "port validation is enabled but port is missing in input url and schemes are not enabled")
}, },
}, },
{
description: "Port enabled but no scheme and https scheme should pass",
options: DomainValidatorOptions{WithPort: true},
expected: "https://example.com",
actual: "https://example.com",
},
{
description: "Port enabled but no scheme and http scheme should pass",
options: DomainValidatorOptions{WithPort: true},
expected: "http://example.com",
actual: "http://example.com",
},
{ {
description: "Port validation with port and no scheme should fail with different port", description: "Port validation with port and no scheme should fail with different port",
options: DomainValidatorOptions{WithPort: true}, options: DomainValidatorOptions{WithPort: true},