From 89be04a45ef1ec20728148198291bc9ed0baed0a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 19:01:50 +0200 Subject: [PATCH 01/44] [deps]: Update dorny/test-reporter action to v2 (#14518) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel GarcĂ­a --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f76b349ccd4..50f04ebaeb1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -58,7 +58,7 @@ jobs: run: npm test -- --coverage --maxWorkers=3 - name: Report test results - uses: dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5 # v1.9.1 + uses: dorny/test-reporter@6e6a65b7a0bd2c9197df7d0ae36ac5cee784230c # v2.0.0 if: ${{ github.event.pull_request.head.repo.full_name == github.repository && !cancelled() }} with: name: Test Results From 0d2c890da11b23948d229070c89050f911fe8852 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 13:29:48 -0400 Subject: [PATCH 02/44] [deps] UI Foundation: Update @types/react to v18.3.20 (#14464) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0db144090b9..bdf9c3a9d26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11820,9 +11820,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", - "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", + "version": "18.3.20", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.20.tgz", + "integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 7c1873090c1..6fe463ac387 100644 --- a/package.json +++ b/package.json @@ -212,7 +212,7 @@ }, "react": "18.3.1", "react-dom": "18.3.1", - "@types/react": "18.3.1", + "@types/react": "18.3.20", "replacestream": "4.0.3" }, "lint-staged": { From 624dafa431f9bf783529e11f35cd665f276eb664 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 13:51:15 -0400 Subject: [PATCH 03/44] [deps] UI Foundation: Update angular-cli monorepo to v18.2.19 (#14465) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 718 +++++++++++++++++++++------------------------- package.json | 6 +- 2 files changed, 333 insertions(+), 391 deletions(-) diff --git a/package-lock.json b/package-lock.json index bdf9c3a9d26..1e7e0741cd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,9 +74,9 @@ "zxcvbn": "4.4.2" }, "devDependencies": { - "@angular-devkit/build-angular": "18.2.12", + "@angular-devkit/build-angular": "18.2.19", "@angular-eslint/schematics": "18.4.3", - "@angular/cli": "18.2.12", + "@angular/cli": "18.2.19", "@angular/compiler-cli": "18.2.13", "@babel/core": "7.24.9", "@babel/preset-env": "7.24.8", @@ -84,7 +84,7 @@ "@electron/notarize": "2.5.0", "@electron/rebuild": "3.7.2", "@lit-labs/signals": "0.1.2", - "@ngtools/webpack": "18.2.12", + "@ngtools/webpack": "18.2.19", "@storybook/addon-a11y": "8.6.12", "@storybook/addon-actions": "8.6.12", "@storybook/addon-designs": "8.2.1", @@ -414,29 +414,28 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.12.tgz", - "integrity": "sha512-quVUi7eqTq9OHumQFNl9Y8t2opm8miu4rlYnuF6rbujmmBDvdUvR6trFChueRczl2p5HWqTOr6NPoDGQm8AyNw==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.19.tgz", + "integrity": "sha512-xwY7v+nGE7TXOc4pgY6u57bLzIPSHuecosYr3TiWHAl9iEcKHzkCCFKsLZyunohHmq/i1uA6g3cC6iwp2xNYyg==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.12", - "@angular-devkit/build-webpack": "0.1802.12", - "@angular-devkit/core": "18.2.12", - "@angular/build": "18.2.12", - "@babel/core": "7.25.2", - "@babel/generator": "7.25.0", - "@babel/helper-annotate-as-pure": "7.24.7", + "@angular-devkit/architect": "0.1802.19", + "@angular-devkit/build-webpack": "0.1802.19", + "@angular-devkit/core": "18.2.19", + "@angular/build": "18.2.19", + "@babel/core": "7.26.10", + "@babel/generator": "7.26.10", + "@babel/helper-annotate-as-pure": "7.25.9", "@babel/helper-split-export-declaration": "7.24.7", - "@babel/plugin-transform-async-generator-functions": "7.25.0", - "@babel/plugin-transform-async-to-generator": "7.24.7", - "@babel/plugin-transform-runtime": "7.24.7", - "@babel/preset-env": "7.25.3", - "@babel/runtime": "7.25.0", + "@babel/plugin-transform-async-generator-functions": "7.26.8", + "@babel/plugin-transform-async-to-generator": "7.25.9", + "@babel/plugin-transform-runtime": "7.26.10", + "@babel/preset-env": "7.26.9", + "@babel/runtime": "7.26.10", "@discoveryjs/json-ext": "0.6.1", - "@ngtools/webpack": "18.2.12", - "@vitejs/plugin-basic-ssl": "1.1.0", + "@ngtools/webpack": "18.2.19", "ansi-colors": "4.1.3", "autoprefixer": "10.4.20", "babel-loader": "9.1.3", @@ -446,7 +445,7 @@ "css-loader": "7.1.2", "esbuild-wasm": "0.23.0", "fast-glob": "3.3.2", - "http-proxy-middleware": "3.0.3", + "http-proxy-middleware": "3.0.5", "https-proxy-agent": "7.0.5", "istanbul-lib-instrument": "6.0.3", "jsonc-parser": "3.3.1", @@ -475,7 +474,6 @@ "terser": "5.31.6", "tree-kill": "1.2.2", "tslib": "2.6.3", - "vite": "5.4.6", "watchpack": "2.4.1", "webpack": "5.94.0", "webpack-dev-middleware": "7.4.2", @@ -543,13 +541,13 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { - "version": "0.1802.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.12.tgz", - "integrity": "sha512-bepVb2/GtJppYKaeW8yTGE6egmoWZ7zagFDsmBdbF+BYp+HmeoPsclARcdryBPVq68zedyTRdvhWSUTbw1AYuw==", + "version": "0.1802.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.19.tgz", + "integrity": "sha512-M4B1tzxGX1nWCZr9GMM8OO0yBJO2HFSdK8M8P74vEFQfKIeq3y16IQ5zlEveJrkCOFVtmlIy2C9foMCdNyBRMA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.12", + "@angular-devkit/core": "18.2.19", "rxjs": "7.8.1" }, "engines": { @@ -559,9 +557,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.12.tgz", - "integrity": "sha512-NtB6ypsaDyPE6/fqWOdfTmACs+yK5RqfH5tStEzWFeeDsIEDYKsJ06ypuRep7qTjYus5Rmttk0Ds+cFgz8JdUQ==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.19.tgz", + "integrity": "sha512-Ptf92Zomc6FCr7GWmHKdgOUbA1GpctZwH/hRcpYpU3tM56MG2t5FOFpufnE595GgolOCktabkFEoODMG8PBVDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -587,22 +585,22 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", - "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -627,95 +625,110 @@ "semver": "bin/semver.js" } }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/preset-env": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", - "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", + "node_modules/@angular-devkit/build-angular/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.2", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/preset-env": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", + "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.0", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.25.0", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.1", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.25.2", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-modules-systemjs": "^7.25.0", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.8", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.26.8", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.26.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.26.8", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.37.1", + "core-js-compat": "^3.40.0", "semver": "^6.3.1" }, "engines": { @@ -827,6 +840,20 @@ "webpack": ">=5" } }, + "node_modules/@angular-devkit/build-angular/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/browserslist": { "version": "4.24.5", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", @@ -1345,9 +1372,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", - "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1416,13 +1443,13 @@ } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1802.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.12.tgz", - "integrity": "sha512-0Z3fdbZVRnjYWE2/VYyfy+uieY+6YZyEp4ylzklVkc+fmLNsnz4Zw6cK1LzzcBqAwKIyh1IdW20Cg7o8b0sONA==", + "version": "0.1802.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.19.tgz", + "integrity": "sha512-axz1Sasn+c+GJpJexBL+B3Rh1w3wJrQq8k8gkniodjJ594p4ti2qGk7i9Tj8A4cXx5fGY+EpuZvKfI/9Tr7QwA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1802.12", + "@angular-devkit/architect": "0.1802.19", "rxjs": "7.8.1" }, "engines": { @@ -1436,13 +1463,13 @@ } }, "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { - "version": "0.1802.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.12.tgz", - "integrity": "sha512-bepVb2/GtJppYKaeW8yTGE6egmoWZ7zagFDsmBdbF+BYp+HmeoPsclARcdryBPVq68zedyTRdvhWSUTbw1AYuw==", + "version": "0.1802.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.19.tgz", + "integrity": "sha512-M4B1tzxGX1nWCZr9GMM8OO0yBJO2HFSdK8M8P74vEFQfKIeq3y16IQ5zlEveJrkCOFVtmlIy2C9foMCdNyBRMA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.12", + "@angular-devkit/core": "18.2.19", "rxjs": "7.8.1" }, "engines": { @@ -1452,9 +1479,9 @@ } }, "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.12.tgz", - "integrity": "sha512-NtB6ypsaDyPE6/fqWOdfTmACs+yK5RqfH5tStEzWFeeDsIEDYKsJ06ypuRep7qTjYus5Rmttk0Ds+cFgz8JdUQ==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.19.tgz", + "integrity": "sha512-Ptf92Zomc6FCr7GWmHKdgOUbA1GpctZwH/hRcpYpU3tM56MG2t5FOFpufnE595GgolOCktabkFEoODMG8PBVDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1581,13 +1608,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "18.2.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.14.tgz", - "integrity": "sha512-mukjZIHHB7gWratq8fZwUq5WZ+1bF4feG/idXr1wgQ+/FqWjs2PP7HDesHVcPymmRulpTyCpB7TNB1O1fgnCpA==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.19.tgz", + "integrity": "sha512-P/0KjkzOf2ZShuShx3cBbjLI7XlcS6B/yCRBo1MQfCC4cZfmzPQoUEOSQeYZgy5pnC24f+dKh/+TWc5uYL/Lvg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.14", + "@angular-devkit/core": "18.2.19", "jsonc-parser": "3.3.1", "magic-string": "0.30.11", "ora": "5.4.1", @@ -1600,9 +1627,9 @@ } }, "node_modules/@angular-devkit/schematics/node_modules/@angular-devkit/core": { - "version": "18.2.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.14.tgz", - "integrity": "sha512-UGIGOjXuOyCW+5S4tINu7e6LOu738CmTw3h7Ui1I8OzdTIYJcYJrei8sgrwDwOYADRal+p0MeMlnykH3TM5XBA==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.19.tgz", + "integrity": "sha512-Ptf92Zomc6FCr7GWmHKdgOUbA1GpctZwH/hRcpYpU3tM56MG2t5FOFpufnE595GgolOCktabkFEoODMG8PBVDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2034,14 +2061,14 @@ } }, "node_modules/@angular/build": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.12.tgz", - "integrity": "sha512-4Ohz+OSILoL+cCAQ4UTiCT5v6pctu3fXNoNpTEUK46OmxELk9jDITO5rNyNS7TxBn9wY69kjX5VcDf7MenquFQ==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.19.tgz", + "integrity": "sha512-dTqR+mhcZWtCRyOafvzHNVpYxMQnt8HHHqNM0kyEMzcztXL2L9zDlKr0H9d+AgGGq/v4qwCh+1gFDxsHByZwMQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.12", + "@angular-devkit/architect": "0.1802.19", "@babel/core": "7.25.2", "@babel/helper-annotate-as-pure": "7.24.7", "@babel/helper-split-export-declaration": "7.24.7", @@ -2063,7 +2090,7 @@ "rollup": "4.22.4", "sass": "1.77.6", "semver": "7.6.3", - "vite": "5.4.6", + "vite": "~5.4.17", "watchpack": "2.4.1" }, "engines": { @@ -2103,13 +2130,13 @@ } }, "node_modules/@angular/build/node_modules/@angular-devkit/architect": { - "version": "0.1802.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.12.tgz", - "integrity": "sha512-bepVb2/GtJppYKaeW8yTGE6egmoWZ7zagFDsmBdbF+BYp+HmeoPsclARcdryBPVq68zedyTRdvhWSUTbw1AYuw==", + "version": "0.1802.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.19.tgz", + "integrity": "sha512-M4B1tzxGX1nWCZr9GMM8OO0yBJO2HFSdK8M8P74vEFQfKIeq3y16IQ5zlEveJrkCOFVtmlIy2C9foMCdNyBRMA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.12", + "@angular-devkit/core": "18.2.19", "rxjs": "7.8.1" }, "engines": { @@ -2119,9 +2146,9 @@ } }, "node_modules/@angular/build/node_modules/@angular-devkit/core": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.12.tgz", - "integrity": "sha512-NtB6ypsaDyPE6/fqWOdfTmACs+yK5RqfH5tStEzWFeeDsIEDYKsJ06ypuRep7qTjYus5Rmttk0Ds+cFgz8JdUQ==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.19.tgz", + "integrity": "sha512-Ptf92Zomc6FCr7GWmHKdgOUbA1GpctZwH/hRcpYpU3tM56MG2t5FOFpufnE595GgolOCktabkFEoODMG8PBVDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2325,18 +2352,18 @@ } }, "node_modules/@angular/cli": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.12.tgz", - "integrity": "sha512-xhuZ/b7IhqNw1MgXf+arWf4x+GfUSt/IwbdWU4+CO8A7h0Y46zQywouP/KUK3cMQZfVdHdciTBvlpF3vFacA6Q==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.19.tgz", + "integrity": "sha512-LGVMTc36JQuw8QX8Sclxyei306EQW3KslopXbf7cfqt6D5/fHS+FqqA0O7V8ob/vOGMca+l6hQD27nW5Y3W6pA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1802.12", - "@angular-devkit/core": "18.2.12", - "@angular-devkit/schematics": "18.2.12", + "@angular-devkit/architect": "0.1802.19", + "@angular-devkit/core": "18.2.19", + "@angular-devkit/schematics": "18.2.19", "@inquirer/prompts": "5.3.8", "@listr2/prompt-adapter-inquirer": "2.0.15", - "@schematics/angular": "18.2.12", + "@schematics/angular": "18.2.19", "@yarnpkg/lockfile": "1.1.0", "ini": "4.1.3", "jsonc-parser": "3.3.1", @@ -2359,13 +2386,13 @@ } }, "node_modules/@angular/cli/node_modules/@angular-devkit/architect": { - "version": "0.1802.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.12.tgz", - "integrity": "sha512-bepVb2/GtJppYKaeW8yTGE6egmoWZ7zagFDsmBdbF+BYp+HmeoPsclARcdryBPVq68zedyTRdvhWSUTbw1AYuw==", + "version": "0.1802.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.19.tgz", + "integrity": "sha512-M4B1tzxGX1nWCZr9GMM8OO0yBJO2HFSdK8M8P74vEFQfKIeq3y16IQ5zlEveJrkCOFVtmlIy2C9foMCdNyBRMA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.12", + "@angular-devkit/core": "18.2.19", "rxjs": "7.8.1" }, "engines": { @@ -2375,9 +2402,9 @@ } }, "node_modules/@angular/cli/node_modules/@angular-devkit/core": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.12.tgz", - "integrity": "sha512-NtB6ypsaDyPE6/fqWOdfTmACs+yK5RqfH5tStEzWFeeDsIEDYKsJ06ypuRep7qTjYus5Rmttk0Ds+cFgz8JdUQ==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.19.tgz", + "integrity": "sha512-Ptf92Zomc6FCr7GWmHKdgOUbA1GpctZwH/hRcpYpU3tM56MG2t5FOFpufnE595GgolOCktabkFEoODMG8PBVDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2402,25 +2429,6 @@ } } }, - "node_modules/@angular/cli/node_modules/@angular-devkit/schematics": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.12.tgz", - "integrity": "sha512-mMea9txHbnCX5lXLHlo0RAgfhFHDio45/jMsREM2PA8UtVf2S8ltXz7ZwUrUyMQRv8vaSfn4ijDstF4hDMnRgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "18.2.12", - "jsonc-parser": "3.3.1", - "magic-string": "0.30.11", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, "node_modules/@angular/cli/node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -2718,14 +2726,14 @@ "license": "ISC" }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -2786,15 +2794,16 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", + "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.0", + "@babel/parser": "^7.26.10", + "@babel/types": "^7.26.10", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" @@ -2916,13 +2925,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz", - "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-annotate-as-pure": "^7.27.1", "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, @@ -2934,13 +2943,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz", + "integrity": "sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.9" + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3031,9 +3040,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -3116,18 +3125,18 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -3158,25 +3167,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", - "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", "license": "MIT", "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", + "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.9" + "@babel/types": "^7.27.1" }, "bin": { "parser": "bin/babel-parser.js" @@ -3616,16 +3625,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", - "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", + "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-remap-async-to-generator": "^7.25.0", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/traverse": "^7.25.0" + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.26.8" }, "engines": { "node": ">=6.9.0" @@ -3635,15 +3643,15 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -4281,6 +4289,23 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-reserved-words": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", @@ -4298,16 +4323,16 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", - "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.10.tgz", + "integrity": "sha512-NWaL2qG6HRpONTnj4JvDU6th4jYeZOJgu3QhmFTCihib0ermtOJqktA5BduGm3suhhVe9EMP9c9+mfJ/I9slqw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, @@ -4318,6 +4343,20 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -4598,9 +4637,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", + "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -4610,30 +4649,30 @@ } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", - "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -4642,13 +4681,13 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", - "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -4657,26 +4696,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/types": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", - "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -5022,23 +5049,6 @@ "semver": "bin/semver.js" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/generator": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", - "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", @@ -5055,42 +5065,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", - "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.26.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", - "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-remap-async-to-generator": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-transform-private-methods": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.7.tgz", @@ -5233,19 +5207,6 @@ "node": ">= 6" } }, - "node_modules/@compodoc/compodoc/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@compodoc/compodoc/node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -8353,9 +8314,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.12.tgz", - "integrity": "sha512-FFJAwtWbtpncMOVNuULPBwFJB7GSjiUwO93eGTzRp8O4EPQ8lCQeFbezQm/NP34+T0+GBLGzPSuQT+muob8YKw==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.19.tgz", + "integrity": "sha512-bExj5JrByKPibsqBbn5Pjn8lo91AUOTsyP2hgKpnOnmSr62rhWSiRwXltgz2MCiZRmuUznpt93WiOLixgYfYvQ==", "dev": true, "license": "MIT", "engines": { @@ -9633,14 +9594,14 @@ "license": "MIT" }, "node_modules/@schematics/angular": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.12.tgz", - "integrity": "sha512-sIoeipsisK5eTLW3XuNZYcal83AfslBbgI7LnV+3VrXwpasKPGHwo2ZdwhCd2IXAkuJ02Iyu7MyV0aQRM9i/3g==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.19.tgz", + "integrity": "sha512-s9aynH/fwB/LT94miVfsaL2C4Qd5BLgjMzWFx7iJ8Hyv7FjOBGYO6eGVovjCt2c6/abG+GQAk4EBOCfg3AUtCA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.12", - "@angular-devkit/schematics": "18.2.12", + "@angular-devkit/core": "18.2.19", + "@angular-devkit/schematics": "18.2.19", "jsonc-parser": "3.3.1" }, "engines": { @@ -9650,9 +9611,9 @@ } }, "node_modules/@schematics/angular/node_modules/@angular-devkit/core": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.12.tgz", - "integrity": "sha512-NtB6ypsaDyPE6/fqWOdfTmACs+yK5RqfH5tStEzWFeeDsIEDYKsJ06ypuRep7qTjYus5Rmttk0Ds+cFgz8JdUQ==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.19.tgz", + "integrity": "sha512-Ptf92Zomc6FCr7GWmHKdgOUbA1GpctZwH/hRcpYpU3tM56MG2t5FOFpufnE595GgolOCktabkFEoODMG8PBVDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9677,25 +9638,6 @@ } } }, - "node_modules/@schematics/angular/node_modules/@angular-devkit/schematics": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.12.tgz", - "integrity": "sha512-mMea9txHbnCX5lXLHlo0RAgfhFHDio45/jMsREM2PA8UtVf2S8ltXz7ZwUrUyMQRv8vaSfn4ijDstF4hDMnRgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "18.2.12", - "jsonc-parser": "3.3.1", - "magic-string": "0.30.11", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, "node_modules/@schematics/angular/node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -21441,9 +21383,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.3.tgz", - "integrity": "sha512-usY0HG5nyDUwtqpiZdETNbmKtw3QQ1jwYFZ9wi5iHzX2BcILwQKtYDJPo7XHTsu5Z0B2Hj3W9NNnbd+AjFWjqg==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.5.tgz", + "integrity": "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==", "dev": true, "license": "MIT", "dependencies": { @@ -24398,15 +24340,15 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-buffer": { @@ -35658,9 +35600,9 @@ } }, "node_modules/vite": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", - "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 6fe463ac387..81f1c92d5a5 100644 --- a/package.json +++ b/package.json @@ -36,9 +36,9 @@ "libs/**/*" ], "devDependencies": { - "@angular-devkit/build-angular": "18.2.12", + "@angular-devkit/build-angular": "18.2.19", "@angular-eslint/schematics": "18.4.3", - "@angular/cli": "18.2.12", + "@angular/cli": "18.2.19", "@angular/compiler-cli": "18.2.13", "@babel/core": "7.24.9", "@babel/preset-env": "7.24.8", @@ -46,7 +46,7 @@ "@electron/notarize": "2.5.0", "@electron/rebuild": "3.7.2", "@lit-labs/signals": "0.1.2", - "@ngtools/webpack": "18.2.12", + "@ngtools/webpack": "18.2.19", "@storybook/addon-a11y": "8.6.12", "@storybook/addon-actions": "8.6.12", "@storybook/addon-designs": "8.2.1", From e538425e7a5ce635b1fbe88835a4f392d31413ee Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 9 May 2025 12:07:14 -0700 Subject: [PATCH 04/44] [PM-21343] - add download bitwarden to list of nudges for settings badge (#14682) * add download bitwarden to list of nudges for settings badge * update tests --- .../src/services/vault-nudges.service.spec.ts | 33 ++++++++++++++----- .../src/services/vault-nudges.service.ts | 2 +- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/libs/vault/src/services/vault-nudges.service.spec.ts b/libs/vault/src/services/vault-nudges.service.spec.ts index 89465fc5382..40c58644309 100644 --- a/libs/vault/src/services/vault-nudges.service.spec.ts +++ b/libs/vault/src/services/vault-nudges.service.spec.ts @@ -12,7 +12,11 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { FakeStateProvider, mockAccountServiceWith } from "../../../common/spec"; -import { HasItemsNudgeService, EmptyVaultNudgeService } from "./custom-nudges-services"; +import { + HasItemsNudgeService, + EmptyVaultNudgeService, + DownloadBitwardenNudgeService, +} from "./custom-nudges-services"; import { DefaultSingleNudgeService } from "./default-single-nudge.service"; import { VaultNudgesService, VaultNudgeType } from "./vault-nudges.service"; @@ -25,6 +29,8 @@ describe("Vault Nudges Service", () => { getFeatureFlag: jest.fn().mockReturnValue(true), }; + const vaultNudgeServices = [EmptyVaultNudgeService, DownloadBitwardenNudgeService]; + beforeEach(async () => { fakeStateProvider = new FakeStateProvider(mockAccountServiceWith("user-id" as UserId)); @@ -46,6 +52,10 @@ describe("Vault Nudges Service", () => { provide: HasItemsNudgeService, useValue: mock(), }, + { + provide: DownloadBitwardenNudgeService, + useValue: mock(), + }, { provide: EmptyVaultNudgeService, useValue: mock(), @@ -130,11 +140,14 @@ describe("Vault Nudges Service", () => { describe("HasActiveBadges", () => { it("should return true if a nudgeType with hasBadgeDismissed === false", async () => { - TestBed.overrideProvider(EmptyVaultNudgeService, { - useValue: { - nudgeStatus$: () => of({ hasBadgeDismissed: false, hasSpotlightDismissed: false }), - }, + vaultNudgeServices.forEach((service) => { + TestBed.overrideProvider(service, { + useValue: { + nudgeStatus$: () => of({ hasBadgeDismissed: false, hasSpotlightDismissed: false }), + }, + }); }); + const service = testBed.inject(VaultNudgesService); const result = await firstValueFrom(service.hasActiveBadges$("user-id" as UserId)); @@ -142,10 +155,12 @@ describe("Vault Nudges Service", () => { expect(result).toBe(true); }); it("should return false if all nudgeTypes have hasBadgeDismissed === true", async () => { - TestBed.overrideProvider(EmptyVaultNudgeService, { - useValue: { - nudgeStatus$: () => of({ hasBadgeDismissed: true, hasSpotlightDismissed: true }), - }, + vaultNudgeServices.forEach((service) => { + TestBed.overrideProvider(service, { + useValue: { + nudgeStatus$: () => of({ hasBadgeDismissed: true, hasSpotlightDismissed: false }), + }, + }); }); const service = testBed.inject(VaultNudgesService); diff --git a/libs/vault/src/services/vault-nudges.service.ts b/libs/vault/src/services/vault-nudges.service.ts index e04cb609d7f..be6e978eaa0 100644 --- a/libs/vault/src/services/vault-nudges.service.ts +++ b/libs/vault/src/services/vault-nudges.service.ts @@ -114,7 +114,7 @@ export class VaultNudgesService { */ hasActiveBadges$(userId: UserId): Observable { // Add more nudge types here if they have the settings badge feature - const nudgeTypes = [VaultNudgeType.EmptyVaultNudge]; + const nudgeTypes = [VaultNudgeType.EmptyVaultNudge, VaultNudgeType.DownloadBitwarden]; const nudgeTypesWithBadge$ = nudgeTypes.map((nudge) => { return this.getNudgeService(nudge) From 6b6f9577f4344daa32767824e9d209e640b5f72e Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 9 May 2025 12:08:12 -0700 Subject: [PATCH 05/44] multiple fixes for bitwarden component (#14685) --- apps/browser/src/_locales/en/messages.json | 6 ++++ .../download-bitwarden.component.html | 36 +++++++++++++------ .../settings/download-bitwarden.component.ts | 3 +- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 1c3214a6ef7..57a0e78fd1b 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -4551,6 +4551,12 @@ "downloadFromBitwardenNow": { "message": "Download from bitwarden.com now" }, + "getItOnGooglePlay": { + "message": "Get it on Google Play" + }, + "downloadOnTheAppStore": { + "message": "Download on the App Store" + }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" }, diff --git a/apps/browser/src/vault/popup/settings/download-bitwarden.component.html b/apps/browser/src/vault/popup/settings/download-bitwarden.component.html index ad063691e76..20896a3c782 100644 --- a/apps/browser/src/vault/popup/settings/download-bitwarden.component.html +++ b/apps/browser/src/vault/popup/settings/download-bitwarden.component.html @@ -2,7 +2,6 @@ -

@@ -20,16 +19,30 @@ /> @@ -41,6 +54,7 @@ {{ "downloadFromBitwardenNow" | i18n }} diff --git a/apps/browser/src/vault/popup/settings/download-bitwarden.component.ts b/apps/browser/src/vault/popup/settings/download-bitwarden.component.ts index b51619b86d1..9f04bb58c34 100644 --- a/apps/browser/src/vault/popup/settings/download-bitwarden.component.ts +++ b/apps/browser/src/vault/popup/settings/download-bitwarden.component.ts @@ -6,7 +6,7 @@ import { firstValueFrom } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { CardComponent, TypographyModule } from "@bitwarden/components"; +import { CardComponent, LinkModule, TypographyModule } from "@bitwarden/components"; import { VaultNudgesService, VaultNudgeType } from "@bitwarden/vault"; import { CurrentAccountComponent } from "../../../auth/popup/account-switching/current-account.component"; @@ -27,6 +27,7 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co CardComponent, TypographyModule, CurrentAccountComponent, + LinkModule, ], }) export class DownloadBitwardenComponent implements OnInit { From 51e327e20bdd44f83af4991b8246ebfc12ed6718 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 21:39:38 +0200 Subject: [PATCH 06/44] Clean up stretchKey (#14520) --- .../services/key-generation.service.spec.ts | 20 +++++++++++++++++++ .../services/key-generation.service.ts | 10 ++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/libs/common/src/platform/services/key-generation.service.spec.ts b/libs/common/src/platform/services/key-generation.service.spec.ts index f75eaeb25be..0a9e997b428 100644 --- a/libs/common/src/platform/services/key-generation.service.spec.ts +++ b/libs/common/src/platform/services/key-generation.service.spec.ts @@ -5,6 +5,7 @@ import { PBKDF2KdfConfig, Argon2KdfConfig } from "@bitwarden/key-management"; import { CryptoFunctionService } from "../../key-management/crypto/abstractions/crypto-function.service"; import { CsprngArray } from "../../types/csprng"; import { EncryptionType } from "../enums"; +import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; import { KeyGenerationService } from "./key-generation.service"; @@ -98,4 +99,23 @@ describe("KeyGenerationService", () => { expect(key.inner().type).toEqual(EncryptionType.AesCbc256_B64); }); }); + + describe("stretchKey", () => { + it("should stretch a key", async () => { + const key = new SymmetricCryptoKey(new Uint8Array(32)); + + cryptoFunctionService.hkdf.mockResolvedValue(new Uint8Array(64)); + + const stretchedKey = await sut.stretchKey(key); + + expect(stretchedKey.inner().type).toEqual(EncryptionType.AesCbc256_HmacSha256_B64); + }); + it("should throw if key is not 32 bytes", async () => { + const key = new SymmetricCryptoKey(new Uint8Array(64)); + + await expect(sut.stretchKey(key)).rejects.toThrow( + "Key passed into stretchKey is not a 256-bit key.", + ); + }); + }); }); diff --git a/libs/common/src/platform/services/key-generation.service.ts b/libs/common/src/platform/services/key-generation.service.ts index 8f9e6856aa0..dcd1f4f95d7 100644 --- a/libs/common/src/platform/services/key-generation.service.ts +++ b/libs/common/src/platform/services/key-generation.service.ts @@ -1,11 +1,11 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { MasterKey, PinKey } from "@bitwarden/common/types/key"; import { KdfConfig, PBKDF2KdfConfig, Argon2KdfConfig, KdfType } from "@bitwarden/key-management"; import { CryptoFunctionService } from "../../key-management/crypto/abstractions/crypto-function.service"; import { CsprngArray } from "../../types/csprng"; import { KeyGenerationService as KeyGenerationServiceAbstraction } from "../abstractions/key-generation.service"; +import { EncryptionType } from "../enums"; import { Utils } from "../misc/utils"; import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; @@ -79,7 +79,13 @@ export class KeyGenerationService implements KeyGenerationServiceAbstraction { return new SymmetricCryptoKey(key); } - async stretchKey(key: MasterKey | PinKey): Promise { + async stretchKey(key: SymmetricCryptoKey): Promise { + // The key to be stretched is actually usually the output of a KDF, and not actually meant for AesCbc256_B64 encryption, + // but has the same key length. Only 256-bit key materials should be stretched. + if (key.inner().type != EncryptionType.AesCbc256_B64) { + throw new Error("Key passed into stretchKey is not a 256-bit key."); + } + const newKey = new Uint8Array(64); // Master key and pin key are always 32 bytes const encKey = await this.cryptoFunctionService.hkdfExpand( From 83dc4706f3605d9dd7cb7c4aa5e529f858d006cf Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Fri, 9 May 2025 16:21:47 -0400 Subject: [PATCH 07/44] [PM-21279] Cipher for url speedup (#14684) * Use `perUserCache$` for `getAllDecryptedForUrl` * Fix loads before first lock * Filter out null ciphers --- libs/common/src/vault/services/cipher.service.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index a32568c8112..169568d44e9 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -519,8 +519,15 @@ export class CipherService implements CipherServiceAbstraction { includeOtherTypes?: CipherType[], defaultMatch: UriMatchStrategySetting = null, ): Promise { - const ciphers = await this.getAllDecrypted(userId); - return await this.filterCiphersForUrl(ciphers, url, includeOtherTypes, defaultMatch); + return await firstValueFrom( + this.cipherViews$(userId).pipe( + filter((c) => c != null), + switchMap( + async (ciphers) => + await this.filterCiphersForUrl(ciphers, url, includeOtherTypes, defaultMatch), + ), + ), + ); } async filterCiphersForUrl( From 5470d6a6d97c9f0e94b901e41ded66bb5ca49168 Mon Sep 17 00:00:00 2001 From: Miles Blackwood Date: Fri, 9 May 2025 17:30:32 -0400 Subject: [PATCH 08/44] Fix i18n key mis-entry. (#14711) --- apps/browser/src/autofill/notification/bar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts index 4b9ba58d586..3fd6016eb33 100644 --- a/apps/browser/src/autofill/notification/bar.ts +++ b/apps/browser/src/autofill/notification/bar.ts @@ -61,7 +61,7 @@ function getI18n() { loginSaveSuccess: chrome.i18n.getMessage("loginSaveSuccess"), loginSaveConfirmation: chrome.i18n.getMessage("loginSaveConfirmation"), loginUpdateSuccess: chrome.i18n.getMessage("loginUpdateSuccess"), - loginUpdateConfirmation: chrome.i18n.getMessage("loginUpdatedConfirmation"), + loginUpdatedConfirmation: chrome.i18n.getMessage("loginUpdatedConfirmation"), loginUpdateTaskSuccess: chrome.i18n.getMessage("loginUpdateTaskSuccess"), loginUpdateTaskSuccessAdditional: chrome.i18n.getMessage("loginUpdateTaskSuccessAdditional"), nextSecurityTaskAction: chrome.i18n.getMessage("nextSecurityTaskAction"), From 1b756df749a23b33f6f69b9740a8c3ce6a6fb5fb Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Fri, 9 May 2025 20:21:25 -0400 Subject: [PATCH 09/44] [PM-14484] `ApiService` showing html in error message (#14658) * Make ApiService more testable * Add ApiService tests * Switch to only reading text/plain content --- .../browser/src/background/main.background.ts | 1 + .../src/platform/services/node-api.service.ts | 1 + libs/angular/src/services/injection-tokens.ts | 3 + .../src/services/jslib-services.module.ts | 6 + libs/common/src/services/api.service.spec.ts | 203 ++++++++++++++++++ libs/common/src/services/api.service.ts | 27 ++- 6 files changed, 230 insertions(+), 11 deletions(-) create mode 100644 libs/common/src/services/api.service.spec.ts diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index d295fddda52..85a9cd27c57 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -720,6 +720,7 @@ export default class MainBackground { this.logService, (logoutReason: LogoutReason, userId?: UserId) => this.logout(logoutReason, userId), this.vaultTimeoutSettingsService, + { createRequest: (url, request) => new Request(url, request) }, ); this.fileUploadService = new FileUploadService(this.logService, this.apiService); diff --git a/apps/cli/src/platform/services/node-api.service.ts b/apps/cli/src/platform/services/node-api.service.ts index 8c7629fb3d9..d695272364b 100644 --- a/apps/cli/src/platform/services/node-api.service.ts +++ b/apps/cli/src/platform/services/node-api.service.ts @@ -39,6 +39,7 @@ export class NodeApiService extends ApiService { logService, logoutCallback, vaultTimeoutSettingsService, + { createRequest: (url, request) => new Request(url, request) }, customUserAgent, ); } diff --git a/libs/angular/src/services/injection-tokens.ts b/libs/angular/src/services/injection-tokens.ts index a63d862b0d8..d82ff021962 100644 --- a/libs/angular/src/services/injection-tokens.ts +++ b/libs/angular/src/services/injection-tokens.ts @@ -13,6 +13,7 @@ import { import { Theme } from "@bitwarden/common/platform/enums"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { Message } from "@bitwarden/common/platform/messaging"; +import { HttpOperations } from "@bitwarden/common/services/api.service"; import { SafeInjectionToken } from "@bitwarden/ui-common"; // Re-export the SafeInjectionToken from ui-common export { SafeInjectionToken } from "@bitwarden/ui-common"; @@ -61,3 +62,5 @@ export const REFRESH_ACCESS_TOKEN_ERROR_CALLBACK = new SafeInjectionToken<() => export const ENV_ADDITIONAL_REGIONS = new SafeInjectionToken( "ENV_ADDITIONAL_REGIONS", ); + +export const HTTP_OPERATIONS = new SafeInjectionToken("HTTP_OPERATIONS"); diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index d82df8574ff..4e7c558a0f0 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -337,6 +337,7 @@ import { CLIENT_TYPE, DEFAULT_VAULT_TIMEOUT, ENV_ADDITIONAL_REGIONS, + HTTP_OPERATIONS, INTRAPROCESS_MESSAGING_SUBJECT, LOCALES_DIRECTORY, LOCKED_CALLBACK, @@ -700,6 +701,10 @@ const safeProviders: SafeProvider[] = [ }, deps: [ToastService, I18nServiceAbstraction], }), + safeProvider({ + provide: HTTP_OPERATIONS, + useValue: { createRequest: (url, request) => new Request(url, request) }, + }), safeProvider({ provide: ApiServiceAbstraction, useClass: ApiService, @@ -712,6 +717,7 @@ const safeProviders: SafeProvider[] = [ LogService, LOGOUT_CALLBACK, VaultTimeoutSettingsService, + HTTP_OPERATIONS, ], }), safeProvider({ diff --git a/libs/common/src/services/api.service.spec.ts b/libs/common/src/services/api.service.spec.ts new file mode 100644 index 00000000000..eca6066b9b7 --- /dev/null +++ b/libs/common/src/services/api.service.spec.ts @@ -0,0 +1,203 @@ +import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { LogoutReason } from "@bitwarden/auth/common"; + +import { TokenService } from "../auth/abstractions/token.service"; +import { DeviceType } from "../enums"; +import { VaultTimeoutSettingsService } from "../key-management/vault-timeout"; +import { ErrorResponse } from "../models/response/error.response"; +import { AppIdService } from "../platform/abstractions/app-id.service"; +import { Environment, EnvironmentService } from "../platform/abstractions/environment.service"; +import { LogService } from "../platform/abstractions/log.service"; +import { PlatformUtilsService } from "../platform/abstractions/platform-utils.service"; + +import { ApiService, HttpOperations } from "./api.service"; + +describe("ApiService", () => { + let tokenService: MockProxy; + let platformUtilsService: MockProxy; + let environmentService: MockProxy; + let appIdService: MockProxy; + let refreshAccessTokenErrorCallback: jest.Mock; + let logService: MockProxy; + let logoutCallback: jest.Mock, [reason: LogoutReason]>; + let vaultTimeoutSettingsService: MockProxy; + let httpOperations: MockProxy; + + let sut: ApiService; + + beforeEach(() => { + tokenService = mock(); + platformUtilsService = mock(); + platformUtilsService.getDevice.mockReturnValue(DeviceType.ChromeExtension); + + environmentService = mock(); + appIdService = mock(); + refreshAccessTokenErrorCallback = jest.fn(); + logService = mock(); + logoutCallback = jest.fn(); + vaultTimeoutSettingsService = mock(); + httpOperations = mock(); + + sut = new ApiService( + tokenService, + platformUtilsService, + environmentService, + appIdService, + refreshAccessTokenErrorCallback, + logService, + logoutCallback, + vaultTimeoutSettingsService, + httpOperations, + "custom-user-agent", + ); + }); + + describe("send", () => { + it("handles ok GET", async () => { + environmentService.environment$ = of({ + getApiUrl: () => "https://example.com", + } satisfies Partial as Environment); + + httpOperations.createRequest.mockImplementation((url, request) => { + return { + url: url, + cache: request.cache, + credentials: request.credentials, + method: request.method, + mode: request.mode, + signal: request.signal, + headers: new Headers(request.headers), + } satisfies Partial as unknown as Request; + }); + + tokenService.getAccessToken.mockResolvedValue("access_token"); + tokenService.tokenNeedsRefresh.mockResolvedValue(false); + + const nativeFetch = jest.fn, [request: Request]>(); + + nativeFetch.mockImplementation((request) => { + return Promise.resolve({ + ok: true, + status: 200, + json: () => Promise.resolve({ hello: "world" }), + headers: new Headers({ + "content-type": "application/json", + }), + } satisfies Partial as Response); + }); + + sut.nativeFetch = nativeFetch; + + const response = await sut.send("GET", "/something", null, true, true, null, null); + + expect(nativeFetch).toHaveBeenCalledTimes(1); + const request = nativeFetch.mock.calls[0][0]; + // This should get set for users of send + expect(request.cache).toBe("no-store"); + // TODO: Could expect on the credentials parameter + expect(request.headers.get("Device-Type")).toBe("2"); // Chrome Extension + // Custom user agent should get set + expect(request.headers.get("User-Agent")).toBe("custom-user-agent"); + // This should be set when the caller has indicated there is a response + expect(request.headers.get("Accept")).toBe("application/json"); + // If they have indicated that it's authed, then the authorization header should get set. + expect(request.headers.get("Authorization")).toBe("Bearer access_token"); + // The response body + expect(response).toEqual({ hello: "world" }); + }); + }); + + const errorData: { + name: string; + input: Partial; + error: Partial; + }[] = [ + { + name: "json response in camel case", + input: { + json: () => Promise.resolve({ message: "Something bad happened." }), + headers: new Headers({ + "content-type": "application/json", + }), + }, + error: { + message: "Something bad happened.", + }, + }, + { + name: "json response in pascal case", + input: { + json: () => Promise.resolve({ Message: "Something bad happened." }), + headers: new Headers({ + "content-type": "application/json", + }), + }, + error: { + message: "Something bad happened.", + }, + }, + { + name: "json response with charset in content type", + input: { + json: () => Promise.resolve({ message: "Something bad happened." }), + headers: new Headers({ + "content-type": "application/json; charset=utf-8", + }), + }, + error: { + message: "Something bad happened.", + }, + }, + { + name: "text/plain response", + input: { + text: () => Promise.resolve("Something bad happened."), + headers: new Headers({ + "content-type": "text/plain", + }), + }, + error: { + message: "Something bad happened.", + }, + }, + ]; + + it.each(errorData)( + "throws error-like response when not ok response with $name", + async ({ input, error }) => { + environmentService.environment$ = of({ + getApiUrl: () => "https://example.com", + } satisfies Partial as Environment); + + httpOperations.createRequest.mockImplementation((url, request) => { + return { + url: url, + cache: request.cache, + credentials: request.credentials, + method: request.method, + mode: request.mode, + signal: request.signal, + headers: new Headers(request.headers), + } satisfies Partial as unknown as Request; + }); + + const nativeFetch = jest.fn, [request: Request]>(); + + nativeFetch.mockImplementation((request) => { + return Promise.resolve({ + ok: false, + status: 400, + ...input, + } satisfies Partial as Response); + }); + + sut.nativeFetch = nativeFetch; + + await expect( + async () => await sut.send("GET", "/something", null, true, true, null, null), + ).rejects.toMatchObject(error); + }, + ); +}); diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index fb4d08db81c..5c4bcdedb26 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -139,6 +139,10 @@ import { AttachmentResponse } from "../vault/models/response/attachment.response import { CipherResponse } from "../vault/models/response/cipher.response"; import { OptionalCipherResponse } from "../vault/models/response/optional-cipher.response"; +export type HttpOperations = { + createRequest: (url: string, request: RequestInit) => Request; +}; + /** * @deprecated The `ApiService` class is deprecated and calls should be extracted into individual * api services. The `send` method is still allowed to be used within api services. For background @@ -166,6 +170,7 @@ export class ApiService implements ApiServiceAbstraction { private logService: LogService, private logoutCallback: (logoutReason: LogoutReason) => Promise, private vaultTimeoutSettingsService: VaultTimeoutSettingsService, + private readonly httpOperations: HttpOperations, private customUserAgent: string = null, ) { this.device = platformUtilsService.getDevice(); @@ -217,7 +222,7 @@ export class ApiService implements ApiServiceAbstraction { const env = await firstValueFrom(this.environmentService.environment$); const response = await this.fetch( - new Request(env.getIdentityUrl() + "/connect/token", { + this.httpOperations.createRequest(env.getIdentityUrl() + "/connect/token", { body: this.qsStringify(identityToken), credentials: await this.getCredentials(), cache: "no-store", @@ -1409,7 +1414,7 @@ export class ApiService implements ApiServiceAbstraction { } const env = await firstValueFrom(this.environmentService.environment$); const response = await this.fetch( - new Request(env.getEventsUrl() + "/collect", { + this.httpOperations.createRequest(env.getEventsUrl() + "/collect", { cache: "no-store", credentials: await this.getCredentials(), method: "POST", @@ -1456,7 +1461,7 @@ export class ApiService implements ApiServiceAbstraction { const authHeader = await this.getActiveBearerToken(); const response = await this.fetch( - new Request(keyConnectorUrl + "/user-keys", { + this.httpOperations.createRequest(keyConnectorUrl + "/user-keys", { cache: "no-store", method: "GET", headers: new Headers({ @@ -1481,7 +1486,7 @@ export class ApiService implements ApiServiceAbstraction { const authHeader = await this.getActiveBearerToken(); const response = await this.fetch( - new Request(keyConnectorUrl + "/user-keys", { + this.httpOperations.createRequest(keyConnectorUrl + "/user-keys", { cache: "no-store", method: "POST", headers: new Headers({ @@ -1501,7 +1506,7 @@ export class ApiService implements ApiServiceAbstraction { async getKeyConnectorAlive(keyConnectorUrl: string) { const response = await this.fetch( - new Request(keyConnectorUrl + "/alive", { + this.httpOperations.createRequest(keyConnectorUrl + "/alive", { cache: "no-store", method: "GET", headers: new Headers({ @@ -1570,7 +1575,7 @@ export class ApiService implements ApiServiceAbstraction { const env = await firstValueFrom(this.environmentService.environment$); const path = `/sso/prevalidate?domainHint=${encodeURIComponent(identifier)}`; const response = await this.fetch( - new Request(env.getIdentityUrl() + path, { + this.httpOperations.createRequest(env.getIdentityUrl() + path, { cache: "no-store", credentials: await this.getCredentials(), headers: headers, @@ -1711,7 +1716,7 @@ export class ApiService implements ApiServiceAbstraction { const env = await firstValueFrom(this.environmentService.environment$); const decodedToken = await this.tokenService.decodeAccessToken(); const response = await this.fetch( - new Request(env.getIdentityUrl() + "/connect/token", { + this.httpOperations.createRequest(env.getIdentityUrl() + "/connect/token", { body: this.qsStringify({ grant_type: "refresh_token", client_id: decodedToken.client_id, @@ -1820,7 +1825,7 @@ export class ApiService implements ApiServiceAbstraction { }; requestInit.headers = requestHeaders; requestInit.body = requestBody; - const response = await this.fetch(new Request(requestUrl, requestInit)); + const response = await this.fetch(this.httpOperations.createRequest(requestUrl, requestInit)); const responseType = response.headers.get("content-type"); const responseIsJson = responseType != null && responseType.indexOf("application/json") !== -1; @@ -1889,7 +1894,7 @@ export class ApiService implements ApiServiceAbstraction { let responseJson: any = null; if (this.isJsonResponse(response)) { responseJson = await response.json(); - } else if (this.isTextResponse(response)) { + } else if (this.isTextPlainResponse(response)) { responseJson = { Message: await response.text() }; } @@ -1945,8 +1950,8 @@ export class ApiService implements ApiServiceAbstraction { return typeHeader != null && typeHeader.indexOf("application/json") > -1; } - private isTextResponse(response: Response): boolean { + private isTextPlainResponse(response: Response): boolean { const typeHeader = response.headers.get("content-type"); - return typeHeader != null && typeHeader.indexOf("text") > -1; + return typeHeader != null && typeHeader.indexOf("text/plain") > -1; } } From 23d4f04b22d52a690de0f6e1ba0918bbcf142c0d Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 9 May 2025 17:51:34 -0700 Subject: [PATCH 10/44] set cipher form to partial edit for non edit permission (#14639) --- apps/desktop/src/vault/app/vault/vault-v2.component.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/desktop/src/vault/app/vault/vault-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-v2.component.ts index 4da2db101ed..66e77580d1c 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -514,6 +514,9 @@ export class VaultV2Component implements OnInit, OnDestroy { this.cipherId = cipher.id; this.cipher = cipher; await this.buildFormConfig("edit"); + if (!cipher.edit && this.config) { + this.config.mode = "partial-edit"; + } this.action = "edit"; await this.go().catch(() => {}); } From 2282a74abda260dd21916c3e330409c2a3e181e8 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 9 May 2025 18:09:26 -0700 Subject: [PATCH 11/44] [PM-21021] - fix toggled hidden field state for custom fields view (#14595) * fix toggled hidden field state for custom fields view * fix hidden field toggle for cards and login details --- .../card-details-view.component.html | 2 ++ .../card-details-view.component.ts | 19 +++++++++++++++++-- .../custom-fields-v2.component.ts | 10 ++++++++-- .../login-credentials-view.component.html | 1 + .../login-credentials-view.component.ts | 19 +++++++++++++++++-- 5 files changed, 45 insertions(+), 6 deletions(-) diff --git a/libs/vault/src/cipher-view/card-details/card-details-view.component.html b/libs/vault/src/cipher-view/card-details/card-details-view.component.html index fff771b6465..ff61addd7db 100644 --- a/libs/vault/src/cipher-view/card-details/card-details-view.component.html +++ b/libs/vault/src/cipher-view/card-details/card-details-view.component.html @@ -33,6 +33,7 @@ bitIconButton bitPasswordInputToggle data-testid="toggle-number" + [toggled]="revealCardNumber" (toggledChange)="logCardEvent($event, EventType.Cipher_ClientToggledCardNumberVisible)" > diff --git a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts index 27d81f32ee6..d99ac438f27 100644 --- a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts +++ b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts @@ -1,7 +1,15 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { CommonModule, DatePipe } from "@angular/common"; -import { Component, EventEmitter, inject, Input, Output } from "@angular/core"; +import { + Component, + EventEmitter, + inject, + Input, + OnChanges, + Output, + SimpleChanges, +} from "@angular/core"; import { Observable, switchMap } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; @@ -51,7 +59,7 @@ type TotpCodeValues = { LinkModule, ], }) -export class LoginCredentialsViewComponent { +export class LoginCredentialsViewComponent implements OnChanges { @Input() cipher: CipherView; @Input() activeUserId: UserId; @Input() hadPendingChangePasswordTask: boolean; @@ -85,6 +93,13 @@ export class LoginCredentialsViewComponent { return `${dateCreated} ${creationDate}`; } + ngOnChanges(changes: SimpleChanges): void { + if (changes["cipher"]) { + this.passwordRevealed = false; + this.showPasswordCount = false; + } + } + async getPremium(organizationId?: string) { await this.premiumUpgradeService.promptForPremium(organizationId); } From 5408a62b7df4631564601fb5fc056e718b6c74f9 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 12 May 2025 11:41:45 +0200 Subject: [PATCH 12/44] [PM-21001] Move KM usage of encrypt service (#14541) * Add new encrypt service functions * Undo changes * Cleanup * Fix build * Fix comments * Move KM usage of encrypt service * Fix build --- .../background/nativeMessaging.background.ts | 5 +-- .../biometric-message-handler.service.spec.ts | 8 ++-- .../biometric-message-handler.service.ts | 4 +- .../crypto/abstractions/encrypt.service.ts | 1 + .../device-trust.service.implementation.ts | 11 +++-- .../services/device-trust.service.spec.ts | 42 ++++++++++--------- .../services/master-password.service.ts | 16 ++----- libs/key-management/src/key.service.spec.ts | 20 +++++---- libs/key-management/src/key.service.ts | 5 +-- ...symmetric-key-regeneration.service.spec.ts | 3 ++ 10 files changed, 59 insertions(+), 56 deletions(-) diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index d92826765db..7172b98d727 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -357,7 +357,7 @@ export class NativeMessagingBackground { await this.secureCommunication(); } - return await this.encryptService.encrypt( + return await this.encryptService.encryptString( JSON.stringify(message), this.secureChannel!.sharedSecret!, ); @@ -401,10 +401,9 @@ export class NativeMessagingBackground { return; } message = JSON.parse( - await this.encryptService.decryptToUtf8( + await this.encryptService.decryptString( rawMessage as EncString, this.secureChannel.sharedSecret, - "ipc-desktop-ipc-channel-key", ), ); } else { diff --git a/apps/desktop/src/services/biometric-message-handler.service.spec.ts b/apps/desktop/src/services/biometric-message-handler.service.spec.ts index 9ddc3da8ed4..af18828b59d 100644 --- a/apps/desktop/src/services/biometric-message-handler.service.spec.ts +++ b/apps/desktop/src/services/biometric-message-handler.service.spec.ts @@ -347,7 +347,7 @@ describe("BiometricMessageHandlerService", () => { trusted: false, }), ); - encryptService.decryptToUtf8.mockResolvedValue( + encryptService.decryptString.mockResolvedValue( JSON.stringify({ command: "biometricUnlock", messageId: 0, @@ -382,7 +382,7 @@ describe("BiometricMessageHandlerService", () => { ngZone.run.mockReturnValue({ closed: of(true), }); - encryptService.decryptToUtf8.mockResolvedValue( + encryptService.decryptString.mockResolvedValue( JSON.stringify({ command: BiometricsCommands.UnlockWithBiometricsForUser, messageId: 0, @@ -433,7 +433,7 @@ describe("BiometricMessageHandlerService", () => { ngZone.run.mockReturnValue({ closed: of(false), }); - encryptService.decryptToUtf8.mockResolvedValue( + encryptService.decryptString.mockResolvedValue( JSON.stringify({ command: BiometricsCommands.UnlockWithBiometricsForUser, messageId: 0, @@ -480,7 +480,7 @@ describe("BiometricMessageHandlerService", () => { trusted: true, }), ); - encryptService.decryptToUtf8.mockResolvedValue( + encryptService.decryptString.mockResolvedValue( JSON.stringify({ command: BiometricsCommands.UnlockWithBiometricsForUser, messageId: 0, diff --git a/apps/desktop/src/services/biometric-message-handler.service.ts b/apps/desktop/src/services/biometric-message-handler.service.ts index 3851f40505b..42d7b8aae5f 100644 --- a/apps/desktop/src/services/biometric-message-handler.service.ts +++ b/apps/desktop/src/services/biometric-message-handler.service.ts @@ -175,7 +175,7 @@ export class BiometricMessageHandlerService { } const message: LegacyMessage = JSON.parse( - await this.encryptService.decryptToUtf8( + await this.encryptService.decryptString( rawMessage as EncString, SymmetricCryptoKey.fromString(sessionSecret), ), @@ -365,7 +365,7 @@ export class BiometricMessageHandlerService { throw new Error("Session secret is missing"); } - const encrypted = await this.encryptService.encrypt( + const encrypted = await this.encryptService.encryptString( JSON.stringify(message), SymmetricCryptoKey.fromString(sessionSecret), ); diff --git a/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts b/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts index 7a6c9bcd800..8bd58a21b6e 100644 --- a/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts +++ b/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts @@ -49,6 +49,7 @@ export abstract class EncryptService { key: SymmetricCryptoKey, decryptTrace?: string, ): Promise; + /** * @deprecated Replaced by BulkEncryptService, remove once the feature is tested and the featureflag PM-4154-multi-worker-encryption-service is removed * @param items The items to decrypt diff --git a/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts b/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts index 06999cab9c3..5e89e0a5cb7 100644 --- a/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts +++ b/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts @@ -209,7 +209,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { devices.data .filter((device) => device.isTrusted) .map(async (device) => { - const publicKey = await this.encryptService.decryptToBytes( + const publicKey = await this.encryptService.unwrapEncapsulationKey( new EncString(device.encryptedPublicKey), oldUserKey, ); @@ -220,7 +220,10 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { return null; } - const newEncryptedPublicKey = await this.encryptService.encrypt(publicKey, newUserKey); + const newEncryptedPublicKey = await this.encryptService.wrapEncapsulationKey( + publicKey, + newUserKey, + ); const newEncryptedUserKey = await this.encryptService.encapsulateKeyUnsigned( newUserKey, publicKey, @@ -278,7 +281,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { const currentDeviceKeys = await this.devicesApiService.getDeviceKeys(deviceIdentifier); // Decrypt the existing device public key with the old user key - const decryptedDevicePublicKey = await this.encryptService.decryptToBytes( + const decryptedDevicePublicKey = await this.encryptService.unwrapEncapsulationKey( currentDeviceKeys.encryptedPublicKey, oldUserKey, ); @@ -394,7 +397,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { try { // attempt to decrypt encryptedDevicePrivateKey with device key - const devicePrivateKey = await this.encryptService.decryptToBytes( + const devicePrivateKey = await this.encryptService.unwrapDecapsulationKey( encryptedDevicePrivateKey, deviceKey, ); diff --git a/libs/common/src/key-management/device-trust/services/device-trust.service.spec.ts b/libs/common/src/key-management/device-trust/services/device-trust.service.spec.ts index 7d4a25c1f08..de9de5d781a 100644 --- a/libs/common/src/key-management/device-trust/services/device-trust.service.spec.ts +++ b/libs/common/src/key-management/device-trust/services/device-trust.service.spec.ts @@ -623,9 +623,9 @@ describe("deviceTrustService", () => { }); it("successfully returns the user key when provided keys (including device key) can decrypt it", async () => { - const decryptToBytesSpy = jest - .spyOn(encryptService, "decryptToBytes") - .mockResolvedValue(new Uint8Array(userKeyBytesLength)); + const unwrapDecapsulationKeySpy = jest + .spyOn(encryptService, "unwrapDecapsulationKey") + .mockResolvedValue(new Uint8Array(2048)); const rsaDecryptSpy = jest .spyOn(encryptService, "decapsulateKeyUnsigned") .mockResolvedValue(new SymmetricCryptoKey(new Uint8Array(userKeyBytesLength))); @@ -638,13 +638,13 @@ describe("deviceTrustService", () => { ); expect(result).toEqual(mockUserKey); - expect(decryptToBytesSpy).toHaveBeenCalledTimes(1); + expect(unwrapDecapsulationKeySpy).toHaveBeenCalledTimes(1); expect(rsaDecryptSpy).toHaveBeenCalledTimes(1); }); it("returns null and removes device key when the decryption fails", async () => { - const decryptToBytesSpy = jest - .spyOn(encryptService, "decryptToBytes") + const unwrapDecapsulationKeySpy = jest + .spyOn(encryptService, "unwrapDecapsulationKey") .mockRejectedValue(new Error("Decryption error")); const setDeviceKeySpy = jest.spyOn(deviceTrustService as any, "setDeviceKey"); @@ -656,7 +656,7 @@ describe("deviceTrustService", () => { ); expect(result).toBeNull(); - expect(decryptToBytesSpy).toHaveBeenCalledTimes(1); + expect(unwrapDecapsulationKeySpy).toHaveBeenCalledTimes(1); expect(setDeviceKeySpy).toHaveBeenCalledTimes(1); expect(setDeviceKeySpy).toHaveBeenCalledWith(mockUserId, null); }); @@ -704,8 +704,8 @@ describe("deviceTrustService", () => { DeviceResponse, ), ); - encryptService.decryptToBytes.mockResolvedValue(null); - encryptService.encrypt.mockResolvedValue(new EncString("test_encrypted_data")); + encryptService.decryptBytes.mockResolvedValue(null); + encryptService.encryptString.mockResolvedValue(new EncString("test_encrypted_data")); encryptService.encapsulateKeyUnsigned.mockResolvedValue( new EncString("test_encrypted_data"), ); @@ -752,9 +752,11 @@ describe("deviceTrustService", () => { DeviceResponse, ), ); - encryptService.decryptToBytes.mockResolvedValue(new Uint8Array(64)); - encryptService.encrypt.mockResolvedValue(new EncString("test_encrypted_data")); - encryptService.rsaEncrypt.mockResolvedValue(new EncString("test_encrypted_data")); + encryptService.unwrapEncapsulationKey.mockResolvedValue(new Uint8Array(64)); + encryptService.wrapEncapsulationKey.mockResolvedValue(new EncString("test_encrypted_data")); + encryptService.encapsulateKeyUnsigned.mockResolvedValue( + new EncString("test_encrypted_data"), + ); const protectedDeviceResponse = new ProtectedDeviceResponse({ id: "", @@ -862,13 +864,15 @@ describe("deviceTrustService", () => { }); // Mock the decryption of the public key with the old user key - encryptService.decryptToBytes.mockImplementationOnce((_encValue, privateKeyValue) => { - expect(privateKeyValue.inner().type).toBe(EncryptionType.AesCbc256_HmacSha256_B64); - expect(new Uint8Array(privateKeyValue.toEncoded())[0]).toBe(FakeOldUserKeyMarker); - const data = new Uint8Array(250); - data.fill(FakeDecryptedPublicKeyMarker, 0, 1); - return Promise.resolve(data); - }); + encryptService.unwrapEncapsulationKey.mockImplementationOnce( + (_encValue, privateKeyValue) => { + expect(privateKeyValue.inner().type).toBe(EncryptionType.AesCbc256_HmacSha256_B64); + expect(new Uint8Array(privateKeyValue.toEncoded())[0]).toBe(FakeOldUserKeyMarker); + const data = new Uint8Array(250); + data.fill(FakeDecryptedPublicKeyMarker, 0, 1); + return Promise.resolve(data); + }, + ); // Mock the encryption of the new user key with the decrypted public key encryptService.encapsulateKeyUnsigned.mockImplementationOnce((data, publicKey) => { diff --git a/libs/common/src/key-management/master-password/services/master-password.service.ts b/libs/common/src/key-management/master-password/services/master-password.service.ts index 72b18d8bfba..b9b11d6cbe8 100644 --- a/libs/common/src/key-management/master-password/services/master-password.service.ts +++ b/libs/common/src/key-management/master-password/services/master-password.service.ts @@ -174,21 +174,13 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr throw new Error("No master key found."); } - let decUserKey: Uint8Array; + let decUserKey: SymmetricCryptoKey; if (userKey.encryptionType === EncryptionType.AesCbc256_B64) { - decUserKey = await this.encryptService.decryptToBytes( - userKey, - masterKey, - "Content: User Key; Encrypting Key: Master Key", - ); + decUserKey = await this.encryptService.unwrapSymmetricKey(userKey, masterKey); } else if (userKey.encryptionType === EncryptionType.AesCbc256_HmacSha256_B64) { const newKey = await this.keyGenerationService.stretchKey(masterKey); - decUserKey = await this.encryptService.decryptToBytes( - userKey, - newKey, - "Content: User Key; Encrypting Key: Stretched Master Key", - ); + decUserKey = await this.encryptService.unwrapSymmetricKey(userKey, newKey); } else { throw new Error("Unsupported encryption type."); } @@ -198,6 +190,6 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr return null; } - return new SymmetricCryptoKey(decUserKey) as UserKey; + return decUserKey as UserKey; } } diff --git a/libs/key-management/src/key.service.spec.ts b/libs/key-management/src/key.service.spec.ts index 57291462924..25d8aff99fb 100644 --- a/libs/key-management/src/key.service.spec.ts +++ b/libs/key-management/src/key.service.spec.ts @@ -330,7 +330,7 @@ describe("keyService", () => { everHadUserKeyState.nextState(null); // Mock private key decryption - encryptService.decryptToBytes.mockResolvedValue(mockRandomBytes); + encryptService.unwrapDecapsulationKey.mockResolvedValue(mockRandomBytes); }); it("throws if userKey is null", async () => { @@ -352,7 +352,7 @@ describe("keyService", () => { }); it("throws if encPrivateKey cannot be decrypted with the userKey", async () => { - encryptService.decryptToBytes.mockResolvedValue(null); + encryptService.unwrapDecapsulationKey.mockResolvedValue(null); await expect( keyService.setUserKeys(mockUserKey, mockEncPrivateKey, mockUserId), @@ -452,17 +452,16 @@ describe("keyService", () => { // Decryption of the user private key const fakeDecryptedUserPrivateKey = makeStaticByteArray(10, 1); - encryptService.decryptToBytes.mockResolvedValue(fakeDecryptedUserPrivateKey); + encryptService.unwrapDecapsulationKey.mockResolvedValue(fakeDecryptedUserPrivateKey); const fakeUserPublicKey = makeStaticByteArray(10, 2); cryptoFunctionService.rsaExtractPublicKey.mockResolvedValue(fakeUserPublicKey); const userPrivateKey = await firstValueFrom(keyService.userPrivateKey$(mockUserId)); - expect(encryptService.decryptToBytes).toHaveBeenCalledWith( + expect(encryptService.unwrapDecapsulationKey).toHaveBeenCalledWith( fakeEncryptedUserPrivateKey, userKey, - "Content: Encrypted Private Key", ); expect(userPrivateKey).toBe(fakeDecryptedUserPrivateKey); @@ -473,7 +472,7 @@ describe("keyService", () => { const userPrivateKey = await firstValueFrom(keyService.userPrivateKey$(mockUserId)); - expect(encryptService.decryptToBytes).not.toHaveBeenCalled(); + expect(encryptService.unwrapDecapsulationKey).not.toHaveBeenCalled(); expect(userPrivateKey).toBeFalsy(); }); @@ -552,10 +551,12 @@ describe("keyService", () => { providerKeysState.nextState(keys.providerKeys!); } - encryptService.decryptToBytes.mockImplementation((encryptedPrivateKey, userKey) => { - // TOOD: Branch between provider and private key? + encryptService.unwrapDecapsulationKey.mockImplementation((encryptedPrivateKey, userKey) => { return Promise.resolve(fakePrivateKeyDecryption(encryptedPrivateKey, userKey)); }); + encryptService.unwrapSymmetricKey.mockImplementation((encryptedPrivateKey, userKey) => { + return Promise.resolve(new SymmetricCryptoKey(new Uint8Array(64))); + }); encryptService.decapsulateKeyUnsigned.mockImplementation((data, privateKey) => { return Promise.resolve(new SymmetricCryptoKey(fakeOrgKeyDecryption(data, privateKey))); @@ -617,6 +618,7 @@ describe("keyService", () => { }); it("returns decryption keys when some of the org keys are providers", async () => { + encryptService.decryptToBytes.mockResolvedValue(new Uint8Array(64)); const org2Id = "org2Id" as OrganizationId; updateKeys({ userKey: makeSymmetricCryptoKey(64), @@ -647,7 +649,7 @@ describe("keyService", () => { const org2Key = decryptionKeys!.orgKeys![org2Id]; expect(org2Key).not.toBeNull(); - expect(org2Key.keyB64).toContain("provider1Key"); + expect(org2Key.toEncoded()).toHaveLength(64); }); it("returns a stream that pays attention to updates of all data", async () => { diff --git a/libs/key-management/src/key.service.ts b/libs/key-management/src/key.service.ts index 01e7b713e1b..849b9db6a50 100644 --- a/libs/key-management/src/key.service.ts +++ b/libs/key-management/src/key.service.ts @@ -738,7 +738,7 @@ export class DefaultKeyService implements KeyServiceAbstraction { const storePin = await this.shouldStoreKey(KeySuffixOptions.Pin, userId); if (storePin) { // Decrypt userKeyEncryptedPin with user key - const pin = await this.encryptService.decryptToUtf8( + const pin = await this.encryptService.decryptString( (await this.pinService.getUserKeyEncryptedPin(userId))!, key, ); @@ -945,10 +945,9 @@ export class DefaultKeyService implements KeyServiceAbstraction { return null; } - return (await this.encryptService.decryptToBytes( + return (await this.encryptService.unwrapDecapsulationKey( new EncString(encryptedPrivateKey), key, - "Content: Encrypted Private Key", )) as UserPrivateKey; } diff --git a/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.spec.ts b/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.spec.ts index 8e2ce8ab6b9..35cef914588 100644 --- a/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.spec.ts +++ b/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.spec.ts @@ -58,6 +58,9 @@ function setupUserKeyValidation( cipher.notes = mockEnc("EncryptedString"); cipher.key = mockEnc("EncKey"); cipherService.getAll.mockResolvedValue([cipher]); + encryptService.unwrapSymmetricKey.mockResolvedValue( + new SymmetricCryptoKey(makeStaticByteArray(64)), + ); encryptService.decryptToBytes.mockResolvedValue(makeStaticByteArray(64)); encryptService.decryptString.mockResolvedValue("mockDecryptedString"); (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); From fcaf5e63c5e8a54dc3c65d0cb16c9c6f26379bfe Mon Sep 17 00:00:00 2001 From: Matt Andreko Date: Mon, 12 May 2025 07:56:50 -0400 Subject: [PATCH 13/44] Duo URL redirect enhancements (#14640) * Provide additional scrutiny on Duo redirect filtering * Address review feedback from Jared * Add documentation to redirectToDuoFrameless method --- apps/web/src/connectors/duo-redirect.spec.ts | 46 ++++++++++++++++++-- apps/web/src/connectors/duo-redirect.ts | 39 ++++++++++++----- 2 files changed, 71 insertions(+), 14 deletions(-) diff --git a/apps/web/src/connectors/duo-redirect.spec.ts b/apps/web/src/connectors/duo-redirect.spec.ts index c0498861ba0..85953a5dd00 100644 --- a/apps/web/src/connectors/duo-redirect.spec.ts +++ b/apps/web/src/connectors/duo-redirect.spec.ts @@ -10,13 +10,13 @@ describe("duo-redirect", () => { }); it("should redirect to a valid Duo URL", () => { - const validUrl = "https://api-123.duosecurity.com/auth"; + const validUrl = "https://api-123.duosecurity.com/oauth/v1/authorize"; redirectToDuoFrameless(validUrl); expect(window.location.href).toBe(validUrl); }); it("should redirect to a valid Duo Federal URL", () => { - const validUrl = "https://api-123.duofederal.com/auth"; + const validUrl = "https://api-123.duofederal.com/oauth/v1/authorize"; redirectToDuoFrameless(validUrl); expect(window.location.href).toBe(validUrl); }); @@ -27,15 +27,55 @@ describe("duo-redirect", () => { }); it("should throw an error for an malicious URL with valid redirect embedded", () => { - const invalidUrl = "https://malicious-site.com\\@api-123.duosecurity.com/auth"; + const invalidUrl = "https://malicious-site.com\\@api-123.duosecurity.com/oauth/v1/authorize"; expect(() => redirectToDuoFrameless(invalidUrl)).toThrow("Invalid redirect URL"); }); + it("should throw an error for a URL with a malicious subdomain", () => { + const maliciousSubdomainUrl = + "https://api-a86d5bde.duosecurity.com.evil.com/oauth/v1/authorize"; + expect(() => redirectToDuoFrameless(maliciousSubdomainUrl)).toThrow("Invalid redirect URL"); + }); + + it("should throw an error for a URL using HTTP protocol", () => { + const maliciousSubdomainUrl = "http://api-a86d5bde.duosecurity.com/oauth/v1/authorize"; + expect(() => redirectToDuoFrameless(maliciousSubdomainUrl)).toThrow( + "Invalid redirect URL: invalid protocol", + ); + }); + + it("should throw an error for a URL with javascript code", () => { + const maliciousSubdomainUrl = "javascript://https://api-a86d5bde.duosecurity.com%0Aalert(1)"; + expect(() => redirectToDuoFrameless(maliciousSubdomainUrl)).toThrow( + "Invalid redirect URL: invalid protocol", + ); + }); + it("should throw an error for a non-HTTPS URL", () => { const nonHttpsUrl = "http://api-123.duosecurity.com/auth"; expect(() => redirectToDuoFrameless(nonHttpsUrl)).toThrow("Invalid redirect URL"); }); + it("should throw an error for a URL with invalid port specified", () => { + const urlWithPort = "https://api-123.duyosecurity.com:8080/auth"; + expect(() => redirectToDuoFrameless(urlWithPort)).toThrow( + "Invalid redirect URL: port not allowed", + ); + }); + + it("should redirect to a valid Duo Federal URL with valid port", () => { + const validUrl = "https://api-123.duofederal.com:443/oauth/v1/authorize"; + redirectToDuoFrameless(validUrl); + expect(window.location.href).toBe(validUrl); + }); + + it("should throw an error for a URL with an invalid pathname", () => { + const urlWithPort = "https://api-123.duyosecurity.com/../evil/path/here/"; + expect(() => redirectToDuoFrameless(urlWithPort)).toThrow( + "Invalid redirect URL: invalid pathname", + ); + }); + it("should throw an error for a URL with an invalid hostname", () => { const invalidHostnameUrl = "https://api-123.invalid.com"; expect(() => redirectToDuoFrameless(invalidHostnameUrl)).toThrow("Invalid redirect URL"); diff --git a/apps/web/src/connectors/duo-redirect.ts b/apps/web/src/connectors/duo-redirect.ts index d1841247962..5389b31f6af 100644 --- a/apps/web/src/connectors/duo-redirect.ts +++ b/apps/web/src/connectors/duo-redirect.ts @@ -57,29 +57,46 @@ window.addEventListener("load", async () => { * @param redirectUrl the duo auth url */ export function redirectToDuoFrameless(redirectUrl: string) { - // Regex to match a valid duo redirect URL + // Validation for Duo redirect URL to prevent open redirect or XSS vulnerabilities + // Only used for Duo 2FA redirects in the extension /** * This regex checks for the following: - * The string must start with "https://api-" - * Followed by a subdomain that can contain letters, numbers + * The hostname must start with a subdomain that begins with "api-" followed by a + * string that can contain letters or numbers of indeterminate length * Followed by either "duosecurity.com" or "duofederal.com" * This ensures that the redirect does not contain any malicious content * and is a valid Duo URL. * */ - const duoRedirectUrlRegex = /^https:\/\/api-[a-zA-Z0-9]+\.(duosecurity|duofederal)\.com/; - // Check if the redirect URL matches the regex - if (!duoRedirectUrlRegex.test(redirectUrl)) { - throw new Error("Invalid redirect URL"); - } - // At this point we know the URL to be valid, but we need to check for embedded credentials + const duoRedirectUrlRegex = /^api-[a-zA-Z0-9]+\.(duosecurity|duofederal)\.com$/; const validateUrl = new URL(redirectUrl); - // URLs should not contain + // Check that no embedded credentials are present if (validateUrl.username || validateUrl.password) { throw new Error("Invalid redirect URL: embedded credentials not allowed"); } - window.location.href = decodeURIComponent(redirectUrl); + // Check that the protocol is HTTPS + if (validateUrl.protocol !== "https:") { + throw new Error("Invalid redirect URL: invalid protocol"); + } + + // Check that the port is not specified + if (validateUrl.port && validateUrl.port !== "443") { + throw new Error("Invalid redirect URL: port not allowed"); + } + + if (validateUrl.pathname !== "/oauth/v1/authorize") { + throw new Error("Invalid redirect URL: invalid pathname"); + } + + // Check if the redirect hostname matches the regex + // Only check the hostname part of the URL to avoid over-zealous Regex expressions from matching + // and causing an Open Redirect vulnerability. Always use hostname instead of host, because host includes port if specified. + if (!duoRedirectUrlRegex.test(validateUrl.hostname)) { + throw new Error("Invalid redirect URL"); + } + + window.location.href = redirectUrl; } /** From 4d15f2d43cbd79ac29b35c8e8e3244ecb59ad8e4 Mon Sep 17 00:00:00 2001 From: Bryan Cunningham Date: Mon, 12 May 2025 09:00:01 -0400 Subject: [PATCH 14/44] [PM-16057] use Roboto as primary font (#14553) * swap to using roboto variable font --- .../content/components/constants/styles.ts | 6 +++--- .../notification/confirmation/message.ts | 2 +- .../components/notification/header-message.ts | 2 +- .../src/autofill/shared/styles/variables.scss | 2 +- apps/browser/src/popup/images/loading.svg | 2 +- apps/browser/src/popup/scss/variables.scss | 2 +- .../src/app/components/avatar.component.ts | 2 +- apps/desktop/src/images/loading.svg | 2 +- apps/desktop/src/scss/variables.scss | 2 +- .../src/app/billing/services/stripe.service.ts | 2 +- apps/web/src/images/loading-white.svg | 2 +- apps/web/src/images/loading.svg | 2 +- apps/web/src/scss/variables.scss | 2 +- libs/angular/src/scss/webfonts.css | 6 +++--- libs/angular/src/scss/webfonts/dm-sans.woff2 | Bin 88504 -> 0 bytes libs/angular/src/scss/webfonts/roboto.woff2 | Bin 0 -> 209484 bytes libs/components/src/avatar/avatar.component.ts | 2 +- libs/components/src/variables.scss | 2 +- 18 files changed, 20 insertions(+), 20 deletions(-) delete mode 100644 libs/angular/src/scss/webfonts/dm-sans.woff2 create mode 100644 libs/angular/src/scss/webfonts/roboto.woff2 diff --git a/apps/browser/src/autofill/content/components/constants/styles.ts b/apps/browser/src/autofill/content/components/constants/styles.ts index f7c9ffd4d92..08c8671ce14 100644 --- a/apps/browser/src/autofill/content/components/constants/styles.ts +++ b/apps/browser/src/autofill/content/components/constants/styles.ts @@ -144,17 +144,17 @@ export const border = { export const typography = { body1: ` line-height: 24px; - font-family: "DM Sans", sans-serif; + font-family: Roboto, sans-serif; font-size: 16px; `, body2: ` line-height: 20px; - font-family: "DM Sans", sans-serif; + font-family: Roboto, sans-serif; font-size: 14px; `, helperMedium: ` line-height: 16px; - font-family: "DM Sans", sans-serif; + font-family: Roboto, sans-serif; font-size: 12px; `, }; diff --git a/apps/browser/src/autofill/content/components/notification/confirmation/message.ts b/apps/browser/src/autofill/content/components/notification/confirmation/message.ts index 521fdf45291..527119aed15 100644 --- a/apps/browser/src/autofill/content/components/notification/confirmation/message.ts +++ b/apps/browser/src/autofill/content/components/notification/confirmation/message.ts @@ -72,7 +72,7 @@ const baseTextStyles = css` text-align: left; text-overflow: ellipsis; line-height: 24px; - font-family: "DM Sans", sans-serif; + font-family: Roboto, sans-serif; font-size: 16px; `; diff --git a/apps/browser/src/autofill/content/components/notification/header-message.ts b/apps/browser/src/autofill/content/components/notification/header-message.ts index ccfa58b8970..47fe8cd2828 100644 --- a/apps/browser/src/autofill/content/components/notification/header-message.ts +++ b/apps/browser/src/autofill/content/components/notification/header-message.ts @@ -19,7 +19,7 @@ const notificationHeaderMessageStyles = (theme: Theme) => css` line-height: 28px; white-space: nowrap; color: ${themes[theme].text.main}; - font-family: "DM Sans", sans-serif; + font-family: Roboto, sans-serif; font-size: 18px; font-weight: 600; `; diff --git a/apps/browser/src/autofill/shared/styles/variables.scss b/apps/browser/src/autofill/shared/styles/variables.scss index ae6a060798a..1e804ed8fd2 100644 --- a/apps/browser/src/autofill/shared/styles/variables.scss +++ b/apps/browser/src/autofill/shared/styles/variables.scss @@ -1,6 +1,6 @@ $dark-icon-themes: "theme_dark"; -$font-family-sans-serif: "DM Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; +$font-family-sans-serif: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif; $font-family-source-code-pro: "Source Code Pro", monospace; $font-size-base: 14px; diff --git a/apps/browser/src/popup/images/loading.svg b/apps/browser/src/popup/images/loading.svg index 3f2033303db..5f4102a5921 100644 --- a/apps/browser/src/popup/images/loading.svg +++ b/apps/browser/src/popup/images/loading.svg @@ -1,5 +1,5 @@  - Loading... diff --git a/apps/browser/src/popup/scss/variables.scss b/apps/browser/src/popup/scss/variables.scss index b78f06f2f3f..aea69e26436 100644 --- a/apps/browser/src/popup/scss/variables.scss +++ b/apps/browser/src/popup/scss/variables.scss @@ -1,6 +1,6 @@ $dark-icon-themes: "theme_dark"; -$font-family-sans-serif: "DM Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; +$font-family-sans-serif: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif; $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; $font-size-base: 16px; $font-size-large: 18px; diff --git a/apps/desktop/src/app/components/avatar.component.ts b/apps/desktop/src/app/components/avatar.component.ts index 87db2e4b6af..d2660763667 100644 --- a/apps/desktop/src/app/components/avatar.component.ts +++ b/apps/desktop/src/app/components/avatar.component.ts @@ -113,7 +113,7 @@ export class AvatarComponent implements OnChanges, OnInit { textTag.setAttribute("fill", Utils.pickTextColorBasedOnBgColor(color, 135, true)); textTag.setAttribute( "font-family", - '"DM Sans","Helvetica Neue",Helvetica,Arial,' + + 'Roboto,"Helvetica Neue",Helvetica,Arial,' + 'sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"', ); textTag.textContent = character; diff --git a/apps/desktop/src/images/loading.svg b/apps/desktop/src/images/loading.svg index 3f2033303db..5f4102a5921 100644 --- a/apps/desktop/src/images/loading.svg +++ b/apps/desktop/src/images/loading.svg @@ -1,5 +1,5 @@  - Loading... diff --git a/apps/desktop/src/scss/variables.scss b/apps/desktop/src/scss/variables.scss index b8978e284e5..b094be63f8c 100644 --- a/apps/desktop/src/scss/variables.scss +++ b/apps/desktop/src/scss/variables.scss @@ -1,6 +1,6 @@ $dark-icon-themes: "theme_dark"; -$font-family-sans-serif: "DM Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; +$font-family-sans-serif: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif; $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; $font-size-base: 14px; $font-size-large: 18px; diff --git a/apps/web/src/app/billing/services/stripe.service.ts b/apps/web/src/app/billing/services/stripe.service.ts index 5c35923c1f4..360187ecd1e 100644 --- a/apps/web/src/app/billing/services/stripe.service.ts +++ b/apps/web/src/app/billing/services/stripe.service.ts @@ -148,7 +148,7 @@ export class StripeService { base: { color: null, fontFamily: - '"DM Sans", "Helvetica Neue", Helvetica, Arial, sans-serif, ' + + 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif, ' + '"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', fontSize: "16px", fontSmoothing: "antialiased", diff --git a/apps/web/src/images/loading-white.svg b/apps/web/src/images/loading-white.svg index 2bebff7daba..ef5970da42e 100644 --- a/apps/web/src/images/loading-white.svg +++ b/apps/web/src/images/loading-white.svg @@ -1,5 +1,5 @@  - Loading... diff --git a/apps/web/src/images/loading.svg b/apps/web/src/images/loading.svg index 3f2033303db..5f4102a5921 100644 --- a/apps/web/src/images/loading.svg +++ b/apps/web/src/images/loading.svg @@ -1,5 +1,5 @@  - Loading... diff --git a/apps/web/src/scss/variables.scss b/apps/web/src/scss/variables.scss index 4b023e12746..66773999c54 100644 --- a/apps/web/src/scss/variables.scss +++ b/apps/web/src/scss/variables.scss @@ -21,7 +21,7 @@ $body-bg: $white; $body-color: #333333; $font-family-sans-serif: - "DM Sans", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; $h1-font-size: 1.7rem; diff --git a/libs/angular/src/scss/webfonts.css b/libs/angular/src/scss/webfonts.css index 04b072e1bf1..cd35271bf80 100644 --- a/libs/angular/src/scss/webfonts.css +++ b/libs/angular/src/scss/webfonts.css @@ -1,8 +1,8 @@ @font-face { - font-family: "DM Sans"; + font-family: Roboto; src: - url("webfonts/dm-sans.woff2") format("woff2 supports variations"), - url("webfonts/dm-sans.woff2") format("woff2-variations"); + url("webfonts/roboto.woff2") format("woff2 supports variations"), + url("webfonts/roboto.woff2") format("woff2-variations"); font-display: swap; font-weight: 100 900; } diff --git a/libs/angular/src/scss/webfonts/dm-sans.woff2 b/libs/angular/src/scss/webfonts/dm-sans.woff2 deleted file mode 100644 index 2375279dbf5a47d05c9b3364961a1f2d768b78cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88504 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�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�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 B`$zx) diff --git a/libs/angular/src/scss/webfonts/roboto.woff2 b/libs/angular/src/scss/webfonts/roboto.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..af8d2343d9c7d72c6fcbf7ffe127d0cc9f3e696c GIT binary patch literal 209484 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
${message || buttonText ? html` - ${itemName} - - ${message || nothing} - ${buttonText - ? html` - handleButtonKeyDown(e, () => handleClick(e))} - aria-label=${buttonAria} - tabindex="0" - role="button" - > - ${buttonText} - - ` - : nothing} - +
+ ${itemName} + + ${message || nothing} + ${buttonText + ? html` + + handleButtonKeyDown(e, () => handleClick(e))} + aria-label=${buttonAria} + tabindex="0" + role="button" + > + ${buttonText} + + ` + : nothing} + +
` : nothing} ${messageDetails @@ -61,12 +64,17 @@ export function NotificationConfirmationMessage({ const containerStyles = css` display: flex; - flex-wrap: wrap; - align-items: center; + flex-direction: column; gap: ${spacing[1]}; width: 100%; `; +const singleLineWrapperStyles = css` + display: inline; + white-space: normal; + word-break: break-word; +`; + const baseTextStyles = css` overflow-x: hidden; text-align: left; @@ -81,6 +89,9 @@ const notificationConfirmationMessageStyles = (theme: Theme) => css` color: ${themes[theme].text.main}; font-weight: 400; + white-space: normal; + word-break: break-word; + display: inline; `; const itemNameStyles = (theme: Theme) => css` @@ -90,6 +101,10 @@ const itemNameStyles = (theme: Theme) => css` font-weight: 400; white-space: nowrap; max-width: 300px; + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + vertical-align: bottom; `; const notificationConfirmationButtonTextStyles = (theme: Theme) => css` diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts index 3fd6016eb33..52cbdcd47d3 100644 --- a/apps/browser/src/autofill/notification/bar.ts +++ b/apps/browser/src/autofill/notification/bar.ts @@ -59,9 +59,7 @@ function getI18n() { collection: chrome.i18n.getMessage("collection"), folder: chrome.i18n.getMessage("folder"), loginSaveSuccess: chrome.i18n.getMessage("loginSaveSuccess"), - loginSaveConfirmation: chrome.i18n.getMessage("loginSaveConfirmation"), loginUpdateSuccess: chrome.i18n.getMessage("loginUpdateSuccess"), - loginUpdatedConfirmation: chrome.i18n.getMessage("loginUpdatedConfirmation"), loginUpdateTaskSuccess: chrome.i18n.getMessage("loginUpdateTaskSuccess"), loginUpdateTaskSuccessAdditional: chrome.i18n.getMessage("loginUpdateTaskSuccessAdditional"), nextSecurityTaskAction: chrome.i18n.getMessage("nextSecurityTaskAction"), @@ -74,6 +72,10 @@ function getI18n() { notificationUpdate: chrome.i18n.getMessage("notificationChangeSave"), notificationEdit: chrome.i18n.getMessage("edit"), notificationEditTooltip: chrome.i18n.getMessage("notificationEditTooltip"), + notificationLoginSaveConfirmation: chrome.i18n.getMessage("notificationLoginSaveConfirmation"), + notificationLoginUpdatedConfirmation: chrome.i18n.getMessage( + "notificationLoginUpdatedConfirmation", + ), notificationUnlock: chrome.i18n.getMessage("notificationUnlock"), notificationUnlockDesc: chrome.i18n.getMessage("notificationUnlockDesc"), notificationViewAria: chrome.i18n.getMessage("notificationViewAria"), From 02cbc37ac56eccefacfb1d35a6a4c7e8f59c0cea Mon Sep 17 00:00:00 2001 From: Daniel Riera Date: Mon, 12 May 2025 17:23:06 -0400 Subject: [PATCH 31/44] PM-20397 Misc notification bar UI tweaks (#14558) * PM-20637 * turncate save button --- .../content/components/notification/container.ts | 2 +- .../content/components/notification/footer.ts | 15 +++++++++------ .../content/components/rows/button-row.ts | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/browser/src/autofill/content/components/notification/container.ts b/apps/browser/src/autofill/content/components/notification/container.ts index c7c7ae16cde..1a78854f5dc 100644 --- a/apps/browser/src/autofill/content/components/notification/container.ts +++ b/apps/browser/src/autofill/content/components/notification/container.ts @@ -94,7 +94,7 @@ const notificationContainerStyles = (theme: Theme) => css` } [class*="${notificationBodyClassPrefix}-"] { - margin: ${spacing["3"]} 0 ${spacing["1.5"]} ${spacing["3"]}; + margin: ${spacing["3"]} 0 0 ${spacing["3"]}; padding-right: ${spacing["3"]}; } `; diff --git a/apps/browser/src/autofill/content/components/notification/footer.ts b/apps/browser/src/autofill/content/components/notification/footer.ts index 73fe9084595..03d0d475e85 100644 --- a/apps/browser/src/autofill/content/components/notification/footer.ts +++ b/apps/browser/src/autofill/content/components/notification/footer.ts @@ -8,7 +8,7 @@ import { NotificationTypes, } from "../../../notification/abstractions/notification-bar"; import { OrgView, FolderView, I18n, CollectionView } from "../common-types"; -import { spacing, themes } from "../constants/styles"; +import { spacing } from "../constants/styles"; import { NotificationButtonRow } from "./button-row"; @@ -37,7 +37,7 @@ export function NotificationFooter({ const primaryButtonText = i18n.saveAction; return html` -
+
${!isChangeNotification ? NotificationButtonRow({ collections, @@ -56,13 +56,16 @@ export function NotificationFooter({ `; } -const notificationFooterStyles = ({ theme }: { theme: Theme }) => css` +const notificationFooterStyles = ({ + isChangeNotification, +}: { + isChangeNotification: boolean; +}) => css` display: flex; - background-color: ${themes[theme].background.alt}; - padding: 0 ${spacing[3]} ${spacing[3]} ${spacing[3]}; + padding: ${spacing[2]} ${spacing[4]} ${isChangeNotification ? spacing[1] : spacing[4]} + ${spacing[4]}; :last-child { border-radius: 0 0 ${spacing["4"]} ${spacing["4"]}; - padding-bottom: ${spacing[4]}; } `; diff --git a/apps/browser/src/autofill/content/components/rows/button-row.ts b/apps/browser/src/autofill/content/components/rows/button-row.ts index 3527d050b81..7e81d846cb7 100644 --- a/apps/browser/src/autofill/content/components/rows/button-row.ts +++ b/apps/browser/src/autofill/content/components/rows/button-row.ts @@ -62,7 +62,7 @@ const buttonRowStyles = css` > button { max-width: min-content; - flex: 1 1 50%; + flex: 1 1 25%; } > div { From 7a51161d50da1b1b4e2428de943a8a83991bde82 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Tue, 13 May 2025 12:43:26 +0200 Subject: [PATCH 32/44] Move selectable avatar to auth (#14704) --- .../settings/account}/selectable-avatar.component.ts | 0 apps/web/src/app/shared/loose-components.module.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename apps/web/src/app/{components => auth/settings/account}/selectable-avatar.component.ts (100%) diff --git a/apps/web/src/app/components/selectable-avatar.component.ts b/apps/web/src/app/auth/settings/account/selectable-avatar.component.ts similarity index 100% rename from apps/web/src/app/components/selectable-avatar.component.ts rename to apps/web/src/app/auth/settings/account/selectable-avatar.component.ts diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index 30d1dd1af75..ab6c6cc5d72 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -22,6 +22,7 @@ import { DangerZoneComponent } from "../auth/settings/account/danger-zone.compon import { DeauthorizeSessionsComponent } from "../auth/settings/account/deauthorize-sessions.component"; import { DeleteAccountDialogComponent } from "../auth/settings/account/delete-account-dialog.component"; import { ProfileComponent } from "../auth/settings/account/profile.component"; +import { SelectableAvatarComponent } from "../auth/settings/account/selectable-avatar.component"; import { EmergencyAccessConfirmComponent } from "../auth/settings/emergency-access/confirm/emergency-access-confirm.component"; import { EmergencyAccessAddEditComponent } from "../auth/settings/emergency-access/emergency-access-add-edit.component"; import { EmergencyAccessComponent } from "../auth/settings/emergency-access/emergency-access.component"; @@ -39,7 +40,6 @@ import { VerifyRecoverDeleteComponent } from "../auth/verify-recover-delete.comp import { SponsoredFamiliesComponent } from "../billing/settings/sponsored-families.component"; import { SponsoringOrgRowComponent } from "../billing/settings/sponsoring-org-row.component"; import { DynamicAvatarComponent } from "../components/dynamic-avatar.component"; -import { SelectableAvatarComponent } from "../components/selectable-avatar.component"; import { ExposedPasswordsReportComponent as OrgExposedPasswordsReportComponent } from "../dirt/reports/pages/organizations/exposed-passwords-report.component"; import { InactiveTwoFactorReportComponent as OrgInactiveTwoFactorReportComponent } from "../dirt/reports/pages/organizations/inactive-two-factor-report.component"; import { ReusedPasswordsReportComponent as OrgReusedPasswordsReportComponent } from "../dirt/reports/pages/organizations/reused-passwords-report.component"; From 4cf6e19b305c693e250f9e4a29008e8f3d06c464 Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Tue, 13 May 2025 09:28:25 -0400 Subject: [PATCH 33/44] Estimate tax for trial initiation flow when trial length is 0 (#14674) --- .../trial-billing-step.component.html | 41 +++++++- .../trial-billing-step.component.ts | 94 ++++++++++++++++++- .../abstractions/tax.service.abstraction.ts | 6 ++ .../src/billing/models/request/tax/index.ts | 1 + ...x-amount-for-organization-trial.request.ts | 11 +++ .../src/billing/models/response/tax/index.ts | 1 + .../tax/preview-tax-amount.response.ts | 11 +++ .../src/billing/services/tax.service.ts | 15 +++ 8 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 libs/common/src/billing/models/request/tax/index.ts create mode 100644 libs/common/src/billing/models/request/tax/preview-tax-amount-for-organization-trial.request.ts create mode 100644 libs/common/src/billing/models/response/tax/index.ts create mode 100644 libs/common/src/billing/models/response/tax/preview-tax-amount.response.ts diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html index d947ea96dfb..64a9781b7cf 100644 --- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html @@ -51,8 +51,38 @@

{{ "paymentType" | i18n }}

+ + @if (trialLength === 0) { + @let priceLabel = + subscriptionProduct === SubscriptionProduct.PasswordManager + ? "passwordManagerPlanPrice" + : "secretsManagerPlanPrice"; + +
+
+ {{ priceLabel | i18n }}: {{ getPriceFor(formGroup.value.cadence) | currency: "USD $" }} +
+ {{ "estimatedTax" | i18n }}: + @if (fetchingTaxAmount) { + + } @else { + {{ taxAmount | currency: "USD $" }} + } +
+
+
+

+ {{ "total" | i18n }}: + @if (fetchingTaxAmount) { + + } @else { + {{ total | currency: "USD $" }}/{{ interval | i18n }} + } +

+
+ }
+ + + + {{ "loading" | i18n }} + diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts index c6248a06a89..614d8bf5f97 100644 --- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts @@ -1,7 +1,16 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from "@angular/core"; +import { + Component, + EventEmitter, + Input, + OnDestroy, + OnInit, + Output, + ViewChild, +} from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; +import { from, Subject, switchMap, takeUntil } from "rxjs"; import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -12,7 +21,14 @@ import { PaymentInformation, PlanInformation, } from "@bitwarden/common/billing/abstractions/organization-billing.service"; -import { PaymentMethodType, PlanType, ProductTierType } from "@bitwarden/common/billing/enums"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; +import { + PaymentMethodType, + PlanType, + ProductTierType, + ProductType, +} from "@bitwarden/common/billing/enums"; +import { PreviewTaxAmountForOrganizationTrialRequest } from "@bitwarden/common/billing/models/request/tax"; import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -50,7 +66,7 @@ export enum SubscriptionProduct { imports: [BillingSharedModule], standalone: true, }) -export class TrialBillingStepComponent implements OnInit { +export class TrialBillingStepComponent implements OnInit, OnDestroy { @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; @ViewChild(ManageTaxInformationComponent) taxInfoComponent: ManageTaxInformationComponent; @Input() organizationInfo: OrganizationInfo; @@ -60,6 +76,7 @@ export class TrialBillingStepComponent implements OnInit { @Output() organizationCreated = new EventEmitter(); loading = true; + fetchingTaxAmount = false; annualCadence = SubscriptionCadence.Annual; monthlyCadence = SubscriptionCadence.Monthly; @@ -73,6 +90,12 @@ export class TrialBillingStepComponent implements OnInit { annualPlan?: PlanResponse; monthlyPlan?: PlanResponse; + taxAmount = 0; + + private destroy$ = new Subject(); + + protected readonly SubscriptionProduct = SubscriptionProduct; + constructor( private apiService: ApiService, private i18nService: I18nService, @@ -80,6 +103,7 @@ export class TrialBillingStepComponent implements OnInit { private messagingService: MessagingService, private organizationBillingService: OrganizationBillingService, private toastService: ToastService, + private taxService: TaxServiceAbstraction, ) {} async ngOnInit(): Promise { @@ -87,9 +111,26 @@ export class TrialBillingStepComponent implements OnInit { this.applicablePlans = plans.data.filter(this.isApplicable); this.annualPlan = this.findPlanFor(SubscriptionCadence.Annual); this.monthlyPlan = this.findPlanFor(SubscriptionCadence.Monthly); + + if (this.trialLength === 0) { + this.formGroup.controls.cadence.valueChanges + .pipe( + switchMap((cadence) => from(this.previewTaxAmount(cadence))), + takeUntil(this.destroy$), + ) + .subscribe((taxAmount) => { + this.taxAmount = taxAmount; + }); + } + this.loading = false; } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + async submit(): Promise { if (!this.taxInfoComponent.validate()) { return; @@ -115,7 +156,11 @@ export class TrialBillingStepComponent implements OnInit { this.messagingService.send("organizationCreated", { organizationId }); } - protected changedCountry() { + async onTaxInformationChanged() { + if (this.trialLength === 0) { + this.taxAmount = await this.previewTaxAmount(this.formGroup.value.cadence); + } + this.paymentComponent.showBankAccount = this.taxInfoComponent.getTaxInformation().country === "US"; if ( @@ -250,4 +295,45 @@ export class TrialBillingStepComponent implements OnInit { const notDisabledOrLegacy = !plan.disabled && !plan.legacyYear; return hasCorrectProductType && notDisabledOrLegacy; } + + private previewTaxAmount = async (cadence: SubscriptionCadence): Promise => { + this.fetchingTaxAmount = true; + + if (!this.taxInfoComponent.validate()) { + return 0; + } + + const plan = this.findPlanFor(cadence); + + const productType = + this.subscriptionProduct === SubscriptionProduct.PasswordManager + ? ProductType.PasswordManager + : ProductType.SecretsManager; + + const taxInformation = this.taxInfoComponent.getTaxInformation(); + + const request: PreviewTaxAmountForOrganizationTrialRequest = { + planType: plan.type, + productType, + taxInformation: { + ...taxInformation, + }, + }; + + const response = await this.taxService.previewTaxAmountForOrganizationTrial(request); + this.fetchingTaxAmount = false; + return response.taxAmount; + }; + + get price() { + return this.getPriceFor(this.formGroup.value.cadence); + } + + get total() { + return this.price + this.taxAmount; + } + + get interval() { + return this.formGroup.value.cadence === SubscriptionCadence.Annual ? "year" : "month"; + } } diff --git a/libs/common/src/billing/abstractions/tax.service.abstraction.ts b/libs/common/src/billing/abstractions/tax.service.abstraction.ts index 7a744dae856..73dc848c95f 100644 --- a/libs/common/src/billing/abstractions/tax.service.abstraction.ts +++ b/libs/common/src/billing/abstractions/tax.service.abstraction.ts @@ -1,7 +1,9 @@ import { CountryListItem } from "../models/domain"; import { PreviewIndividualInvoiceRequest } from "../models/request/preview-individual-invoice.request"; import { PreviewOrganizationInvoiceRequest } from "../models/request/preview-organization-invoice.request"; +import { PreviewTaxAmountForOrganizationTrialRequest } from "../models/request/tax"; import { PreviewInvoiceResponse } from "../models/response/preview-invoice.response"; +import { PreviewTaxAmountResponse } from "../models/response/tax"; export abstract class TaxServiceAbstraction { abstract getCountries(): CountryListItem[]; @@ -15,4 +17,8 @@ export abstract class TaxServiceAbstraction { abstract previewOrganizationInvoice( request: PreviewOrganizationInvoiceRequest, ): Promise; + + abstract previewTaxAmountForOrganizationTrial: ( + request: PreviewTaxAmountForOrganizationTrialRequest, + ) => Promise; } diff --git a/libs/common/src/billing/models/request/tax/index.ts b/libs/common/src/billing/models/request/tax/index.ts new file mode 100644 index 00000000000..cda1930c614 --- /dev/null +++ b/libs/common/src/billing/models/request/tax/index.ts @@ -0,0 +1 @@ +export * from "./preview-tax-amount-for-organization-trial.request"; diff --git a/libs/common/src/billing/models/request/tax/preview-tax-amount-for-organization-trial.request.ts b/libs/common/src/billing/models/request/tax/preview-tax-amount-for-organization-trial.request.ts new file mode 100644 index 00000000000..3f366335a47 --- /dev/null +++ b/libs/common/src/billing/models/request/tax/preview-tax-amount-for-organization-trial.request.ts @@ -0,0 +1,11 @@ +import { PlanType, ProductType } from "../../../enums"; + +export type PreviewTaxAmountForOrganizationTrialRequest = { + planType: PlanType; + productType: ProductType; + taxInformation: { + country: string; + postalCode: string; + taxId?: string; + }; +}; diff --git a/libs/common/src/billing/models/response/tax/index.ts b/libs/common/src/billing/models/response/tax/index.ts new file mode 100644 index 00000000000..525d6d7c80a --- /dev/null +++ b/libs/common/src/billing/models/response/tax/index.ts @@ -0,0 +1 @@ +export * from "./preview-tax-amount.response"; diff --git a/libs/common/src/billing/models/response/tax/preview-tax-amount.response.ts b/libs/common/src/billing/models/response/tax/preview-tax-amount.response.ts new file mode 100644 index 00000000000..cf15156551a --- /dev/null +++ b/libs/common/src/billing/models/response/tax/preview-tax-amount.response.ts @@ -0,0 +1,11 @@ +import { BaseResponse } from "../../../../models/response/base.response"; + +export class PreviewTaxAmountResponse extends BaseResponse { + taxAmount: number; + + constructor(response: any) { + super(response); + + this.taxAmount = this.getResponseProperty("TaxAmount"); + } +} diff --git a/libs/common/src/billing/services/tax.service.ts b/libs/common/src/billing/services/tax.service.ts index aa27c99adc8..2632ca7083b 100644 --- a/libs/common/src/billing/services/tax.service.ts +++ b/libs/common/src/billing/services/tax.service.ts @@ -1,3 +1,6 @@ +import { PreviewTaxAmountForOrganizationTrialRequest } from "@bitwarden/common/billing/models/request/tax"; +import { PreviewTaxAmountResponse } from "@bitwarden/common/billing/models/response/tax"; + import { ApiService } from "../../abstractions/api.service"; import { TaxServiceAbstraction } from "../abstractions/tax.service.abstraction"; import { CountryListItem } from "../models/domain"; @@ -300,4 +303,16 @@ export class TaxService implements TaxServiceAbstraction { ); return new PreviewInvoiceResponse(response); } + + async previewTaxAmountForOrganizationTrial( + request: PreviewTaxAmountForOrganizationTrialRequest, + ): Promise { + return await this.apiService.send( + "POST", + "/tax/preview-amount/organization-trial", + request, + true, + true, + ); + } } From 992b1456a82ef5626501bbdf35277d3b8078a4db Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Tue, 13 May 2025 09:31:47 -0400 Subject: [PATCH 34/44] Send SkipTrial = true to organization create when trial length is 0 (#14701) --- .../accounts/trial-initiation/trial-billing-step.component.ts | 1 + .../admin-console/models/request/organization-create.request.ts | 1 + .../src/billing/abstractions/organization-billing.service.ts | 1 + libs/common/src/billing/services/organization-billing.service.ts | 1 + 4 files changed, 4 insertions(+) diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts index 614d8bf5f97..9f910ad9d23 100644 --- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts @@ -215,6 +215,7 @@ export class TrialBillingStepComponent implements OnInit, OnDestroy { const payment: PaymentInformation = { paymentMethod, billing: this.getBillingInformationFromTaxInfoComponent(), + skipTrial: this.trialLength === 0, }; const response = await this.organizationBillingService.purchaseSubscription({ diff --git a/libs/common/src/admin-console/models/request/organization-create.request.ts b/libs/common/src/admin-console/models/request/organization-create.request.ts index e8561307b20..d9c62f1e20a 100644 --- a/libs/common/src/admin-console/models/request/organization-create.request.ts +++ b/libs/common/src/admin-console/models/request/organization-create.request.ts @@ -6,4 +6,5 @@ import { OrganizationNoPaymentMethodCreateRequest } from "../../../billing/model export class OrganizationCreateRequest extends OrganizationNoPaymentMethodCreateRequest { paymentMethodType: PaymentMethodType; paymentToken: string; + skipTrial?: boolean; } diff --git a/libs/common/src/billing/abstractions/organization-billing.service.ts b/libs/common/src/billing/abstractions/organization-billing.service.ts index 8024a120b0a..58c537c99cc 100644 --- a/libs/common/src/billing/abstractions/organization-billing.service.ts +++ b/libs/common/src/billing/abstractions/organization-billing.service.ts @@ -39,6 +39,7 @@ export type BillingInformation = { export type PaymentInformation = { paymentMethod: [string, PaymentMethodType]; billing: BillingInformation; + skipTrial?: boolean; }; export type SubscriptionInformation = { diff --git a/libs/common/src/billing/services/organization-billing.service.ts b/libs/common/src/billing/services/organization-billing.service.ts index c6bd88d8dd6..fe5623fd5e6 100644 --- a/libs/common/src/billing/services/organization-billing.service.ts +++ b/libs/common/src/billing/services/organization-billing.service.ts @@ -174,6 +174,7 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs const [paymentToken, paymentMethodType] = information.paymentMethod; request.paymentToken = paymentToken; request.paymentMethodType = paymentMethodType; + request.skipTrial = information.skipTrial; const billingInformation = information.billing; request.billingAddressPostalCode = billingInformation.postalCode; From b2c118d607c7be1298b39cd6c3ea3012411e5a78 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 13 May 2025 15:41:57 +0200 Subject: [PATCH 35/44] Move admin-console code to new encrypt service interface (#14547) --- .../organization-user-reset-password.service.ts | 2 +- .../providers/services/web-provider.service.ts | 2 +- .../services/default-collection-admin.service.ts | 4 ++-- .../collections/services/default-collection.service.spec.ts | 5 ++++- .../collections/services/default-collection.service.ts | 2 +- .../services/default-vnext-collection.service.spec.ts | 6 ++++++ .../services/default-vnext-collection.service.ts | 2 +- .../models/domain/encrypted-organization-key.ts | 4 ++-- 8 files changed, 18 insertions(+), 9 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts index 78d2d8fd165..ecf4d26eb52 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts @@ -112,7 +112,7 @@ export class OrganizationUserResetPasswordService if (orgSymKey == null) { throw new Error("No org key found"); } - const decPrivateKey = await this.encryptService.decryptToBytes( + const decPrivateKey = await this.encryptService.unwrapDecapsulationKey( new EncString(response.encryptedPrivateKey), orgSymKey, ); diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts index 844c6b779a9..418b7020ff9 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts @@ -74,7 +74,7 @@ export class WebProviderService { const [publicKey, encryptedPrivateKey] = await this.keyService.makeKeyPair(organizationKey); - const encryptedCollectionName = await this.encryptService.encrypt( + const encryptedCollectionName = await this.encryptService.encryptString( this.i18nService.t("defaultCollection"), organizationKey, ); diff --git a/libs/admin-console/src/common/collections/services/default-collection-admin.service.ts b/libs/admin-console/src/common/collections/services/default-collection-admin.service.ts index 890353d9039..293090ce315 100644 --- a/libs/admin-console/src/common/collections/services/default-collection-admin.service.ts +++ b/libs/admin-console/src/common/collections/services/default-collection-admin.service.ts @@ -116,7 +116,7 @@ export class DefaultCollectionAdminService implements CollectionAdminService { const promises = collections.map(async (c) => { const view = new CollectionAdminView(); view.id = c.id; - view.name = await this.encryptService.decryptToUtf8(new EncString(c.name), orgKey); + view.name = await this.encryptService.decryptString(new EncString(c.name), orgKey); view.externalId = c.externalId; view.organizationId = c.organizationId; @@ -146,7 +146,7 @@ export class DefaultCollectionAdminService implements CollectionAdminService { } const collection = new CollectionRequest(); collection.externalId = model.externalId; - collection.name = (await this.encryptService.encrypt(model.name, key)).encryptedString; + collection.name = (await this.encryptService.encryptString(model.name, key)).encryptedString; collection.groups = model.groups.map( (group) => new SelectionReadOnlyRequest(group.id, group.readOnly, group.hidePasswords, group.manage), diff --git a/libs/admin-console/src/common/collections/services/default-collection.service.spec.ts b/libs/admin-console/src/common/collections/services/default-collection.service.spec.ts index 7fe81ade4d2..c5f57f38dd3 100644 --- a/libs/admin-console/src/common/collections/services/default-collection.service.spec.ts +++ b/libs/admin-console/src/common/collections/services/default-collection.service.spec.ts @@ -120,9 +120,12 @@ const mockStateProvider = () => { const mockCryptoService = () => { const keyService = mock(); const encryptService = mock(); - encryptService.decryptToUtf8 + encryptService.decryptString .calledWith(expect.any(EncString), expect.anything()) .mockResolvedValue("DECRYPTED_STRING"); + encryptService.decryptToUtf8 + .calledWith(expect.any(EncString), expect.anything(), expect.anything()) + .mockResolvedValue("DECRYPTED_STRING"); (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); diff --git a/libs/admin-console/src/common/collections/services/default-collection.service.ts b/libs/admin-console/src/common/collections/services/default-collection.service.ts index 1ae58d3eef3..a1dd0419e2c 100644 --- a/libs/admin-console/src/common/collections/services/default-collection.service.ts +++ b/libs/admin-console/src/common/collections/services/default-collection.service.ts @@ -113,7 +113,7 @@ export class DefaultCollectionService implements CollectionService { collection.organizationId = model.organizationId; collection.readOnly = model.readOnly; collection.externalId = model.externalId; - collection.name = await this.encryptService.encrypt(model.name, key); + collection.name = await this.encryptService.encryptString(model.name, key); return collection; } diff --git a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts index 9700fcb695a..d4bc026b5bd 100644 --- a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts +++ b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts @@ -46,6 +46,11 @@ describe("DefaultvNextCollectionService", () => { keyService.orgKeys$.mockReturnValue(cryptoKeys); // Set up mock decryption + encryptService.decryptString + .calledWith(expect.any(EncString), expect.any(SymmetricCryptoKey)) + .mockImplementation((encString, key) => + Promise.resolve(encString.data.replace("ENC_", "DEC_")), + ); encryptService.decryptToUtf8 .calledWith(expect.any(EncString), expect.any(SymmetricCryptoKey), expect.any(String)) .mockImplementation((encString, key) => @@ -103,6 +108,7 @@ describe("DefaultvNextCollectionService", () => { ]); // Assert that the correct org keys were used for each encrypted string + // This should be replaced with decryptString when the platform PR (https://github.com/bitwarden/clients/pull/14544) is merged expect(encryptService.decryptToUtf8).toHaveBeenCalledWith( expect.objectContaining(new EncString(collection1.name)), orgKey1, diff --git a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.ts b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.ts index 0ef8ae99ab3..4dcda795afe 100644 --- a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.ts +++ b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.ts @@ -113,7 +113,7 @@ export class DefaultvNextCollectionService implements vNextCollectionService { collection.organizationId = model.organizationId; collection.readOnly = model.readOnly; collection.externalId = model.externalId; - collection.name = await this.encryptService.encrypt(model.name, key); + collection.name = await this.encryptService.encryptString(model.name, key); return collection; } diff --git a/libs/common/src/admin-console/models/domain/encrypted-organization-key.ts b/libs/common/src/admin-console/models/domain/encrypted-organization-key.ts index 984d80ba519..297bcf08d8c 100644 --- a/libs/common/src/admin-console/models/domain/encrypted-organization-key.ts +++ b/libs/common/src/admin-console/models/domain/encrypted-organization-key.ts @@ -56,14 +56,14 @@ export class ProviderEncryptedOrganizationKey implements BaseEncryptedOrganizati ) {} async decrypt(encryptService: EncryptService, providerKeys: Record) { - const decValue = await encryptService.decryptToBytes( + const decValue = await encryptService.unwrapSymmetricKey( new EncString(this.key), providerKeys[this.providerId], ); if (decValue == null) { throw new Error("Failed to decrypt organization key"); } - return new SymmetricCryptoKey(decValue) as OrgKey; + return decValue as OrgKey; } get encryptedOrganizationKey() { From 00beef617caf0bc3aa81efdc5d4f34e53c1f2e08 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 13 May 2025 15:42:48 +0200 Subject: [PATCH 36/44] [PM-21586] Return null in decryptUserKeyWithMasterKey if decrypt fails (#14756) * Return null in decryptUserKeyWithMasterKey if decrypt fails * Show error on invalid master password * Add logs --- .../master-password.service.abstraction.ts | 4 ++-- .../services/master-password.service.ts | 20 ++++++++++++++----- .../src/lock/components/lock.component.ts | 9 +++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/libs/common/src/key-management/master-password/abstractions/master-password.service.abstraction.ts b/libs/common/src/key-management/master-password/abstractions/master-password.service.abstraction.ts index 221ce8ed6ef..fded0cea023 100644 --- a/libs/common/src/key-management/master-password/abstractions/master-password.service.abstraction.ts +++ b/libs/common/src/key-management/master-password/abstractions/master-password.service.abstraction.ts @@ -37,13 +37,13 @@ export abstract class MasterPasswordServiceAbstraction { * @param userKey The user's encrypted symmetric key * @throws If either the MasterKey or UserKey are not resolved, or if the UserKey encryption type * is neither AesCbc256_B64 nor AesCbc256_HmacSha256_B64 - * @returns The user key + * @returns The user key or null if the masterkey is wrong */ abstract decryptUserKeyWithMasterKey: ( masterKey: MasterKey, userId: string, userKey?: EncString, - ) => Promise; + ) => Promise; } export abstract class InternalMasterPasswordServiceAbstraction extends MasterPasswordServiceAbstraction { diff --git a/libs/common/src/key-management/master-password/services/master-password.service.ts b/libs/common/src/key-management/master-password/services/master-password.service.ts index b9b11d6cbe8..9e58680d453 100644 --- a/libs/common/src/key-management/master-password/services/master-password.service.ts +++ b/libs/common/src/key-management/master-password/services/master-password.service.ts @@ -166,7 +166,7 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr masterKey: MasterKey, userId: UserId, userKey?: EncString, - ): Promise { + ): Promise { userKey ??= await this.getMasterKeyEncryptedUserKey(userId); masterKey ??= await firstValueFrom(this.masterKey$(userId)); @@ -177,16 +177,26 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr let decUserKey: SymmetricCryptoKey; if (userKey.encryptionType === EncryptionType.AesCbc256_B64) { - decUserKey = await this.encryptService.unwrapSymmetricKey(userKey, masterKey); + try { + decUserKey = await this.encryptService.unwrapSymmetricKey(userKey, masterKey); + } catch { + this.logService.warning("Failed to decrypt user key with master key."); + return null; + } } else if (userKey.encryptionType === EncryptionType.AesCbc256_HmacSha256_B64) { - const newKey = await this.keyGenerationService.stretchKey(masterKey); - decUserKey = await this.encryptService.unwrapSymmetricKey(userKey, newKey); + try { + const newKey = await this.keyGenerationService.stretchKey(masterKey); + decUserKey = await this.encryptService.unwrapSymmetricKey(userKey, newKey); + } catch { + this.logService.warning("Failed to decrypt user key with stretched master key."); + return null; + } } else { throw new Error("Unsupported encryption type."); } if (decUserKey == null) { - this.logService.warning("Failed to decrypt user key with master key."); + this.logService.warning("Failed to decrypt user key with master key, user key is null."); return null; } diff --git a/libs/key-management-ui/src/lock/components/lock.component.ts b/libs/key-management-ui/src/lock/components/lock.component.ts index 80d64e17b84..3cb0dbaca52 100644 --- a/libs/key-management-ui/src/lock/components/lock.component.ts +++ b/libs/key-management-ui/src/lock/components/lock.component.ts @@ -556,6 +556,15 @@ export class LockComponent implements OnInit, OnDestroy { masterPasswordVerificationResponse!.masterKey, this.activeAccount.id, ); + if (userKey == null) { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("invalidMasterPassword"), + }); + return; + } + await this.setUserKeyAndContinue(userKey, true); } From 9f3310ed7edecb2afc68a284dedd37f68dae72f9 Mon Sep 17 00:00:00 2001 From: Tom <144813356+ttalty@users.noreply.github.com> Date: Tue, 13 May 2025 09:44:54 -0400 Subject: [PATCH 37/44] Redoing the PR for get by domain due to file move (#14746) --- .../services/risk-insights-report.service.spec.ts | 10 +++++----- .../services/risk-insights-report.service.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.spec.ts index 705eb1231a9..f9177bf1bf7 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.spec.ts @@ -50,7 +50,7 @@ describe("RiskInsightsReportService", () => { let testCase = testCaseResults[0]; expect(testCase).toBeTruthy(); expect(testCase.cipherMembers).toHaveLength(2); - expect(testCase.trimmedUris).toHaveLength(3); + expect(testCase.trimmedUris).toHaveLength(2); expect(testCase.weakPasswordDetail).toBeTruthy(); expect(testCase.exposedPasswordDetail).toBeTruthy(); expect(testCase.reusedPasswordCount).toEqual(2); @@ -69,7 +69,7 @@ describe("RiskInsightsReportService", () => { it("should generate the raw data + uri report correctly", async () => { const result = await firstValueFrom(service.generateRawDataUriReport$("orgId")); - expect(result).toHaveLength(9); + expect(result).toHaveLength(8); // Two ciphers that have google.com as their uri. There should be 2 results const googleResults = result.filter((x) => x.trimmedUri === "google.com"); @@ -88,7 +88,7 @@ describe("RiskInsightsReportService", () => { it("should generate applications health report data correctly", async () => { const result = await firstValueFrom(service.generateApplicationsReport$("orgId")); - expect(result).toHaveLength(6); + expect(result).toHaveLength(5); // Two ciphers have google.com associated with them. The first cipher // has 2 members and the second has 4. However, the 2 members in the first @@ -132,7 +132,7 @@ describe("RiskInsightsReportService", () => { expect(reportSummary.totalMemberCount).toEqual(7); expect(reportSummary.totalAtRiskMemberCount).toEqual(6); - expect(reportSummary.totalApplicationCount).toEqual(6); - expect(reportSummary.totalAtRiskApplicationCount).toEqual(5); + expect(reportSummary.totalApplicationCount).toEqual(5); + expect(reportSummary.totalAtRiskApplicationCount).toEqual(4); }); }); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts index 027760f678c..8a6eb5000cd 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts @@ -428,7 +428,7 @@ export class RiskInsightsReportService { const cipherUris: string[] = []; const uris = cipher.login?.uris ?? []; uris.map((u: { uri: string }) => { - const uri = Utils.getHostname(u.uri).replace("www.", ""); + const uri = Utils.getDomain(u.uri); if (!cipherUris.includes(uri)) { cipherUris.push(uri); } From 0b0397c3f05d94e1ea8cbedd97b7706fba968e9b Mon Sep 17 00:00:00 2001 From: Patrick-Pimentel-Bitwarden Date: Tue, 13 May 2025 10:07:38 -0400 Subject: [PATCH 38/44] fix(enums-eslint): Enum Rule for ESLint (#14650) * fix(enums-eslint): Enum Rule for ESLint - Added enums in the warnings for eslint. * fix(enums-eslint): Enum Rule for ESLint - Updated to error in both places for enums. * fix(enums-eslint): Enum Rule for ESLint - Added new eslint plugin for warning on enums. * fix(enums-eslint): Enum Rule for ESLint - Changed based on suggestion. Co-authored-by: Andreas Coroiu * refactor(browser-platform-utils): Remove Deprecation and Fix Code - Changed usages of firefox to private and moved the usages to the preferred public method and removed the deprecations. * fix(enums-eslint): Enum Rule for ESLint - Updated to error and added disable rules for all other places. * fix(enums-eslint): Enum Rule for ESLint - Undid other changes by accident --- .../autofill/enums/autofill-overlay.enum.ts | 2 + .../fido2/content/messaging/message.ts | 2 + .../tools/popup/send-v2/send-v2.component.ts | 2 + .../at-risk-carousel-dialog.component.ts | 2 + .../vault-generator-dialog.component.ts | 2 + .../components/vault-v2/vault-v2.component.ts | 2 + .../src/ipc.service.ts | 2 + .../src/app/tools/send/send.component.ts | 2 + .../src/autofill/models/ssh-agent-setting.ts | 2 + apps/desktop/src/types/biometric-message.ts | 2 + .../credential-generator-dialog.component.ts | 2 + .../bulk-collections-dialog.component.ts | 2 + .../collections/vault.component.ts | 2 + .../manage/group-add-edit.component.ts | 4 ++ .../member-dialog/member-dialog.component.ts | 4 ++ .../components/reset-password.component.ts | 2 + .../policies/policy-edit.component.ts | 2 + .../delete-organization-dialog.component.ts | 2 + .../access-selector.component.ts | 2 + .../access-selector/access-selector.models.ts | 4 ++ .../collection-dialog.component.ts | 6 ++ ...bauthn-login-credential-prf-status.enum.ts | 2 + .../enums/emergency-access-status-type.ts | 2 + .../enums/emergency-access-type.ts | 2 + .../emergency-access-confirm.component.ts | 2 + .../emergency-access-add-edit.component.ts | 2 + .../emergency-access-takeover.component.ts | 2 + .../create-credential-dialog.component.ts | 2 + .../trial-billing-step.component.ts | 4 ++ .../change-plan-dialog.component.ts | 4 ++ .../download-license.component.ts | 2 + ...ization-subscription-selfhost.component.ts | 2 + .../shared/add-credit-dialog.component.ts | 2 + .../adjust-payment-dialog.component.ts | 2 + .../adjust-storage-dialog.component.ts | 2 + .../shared/offboarding-survey.component.ts | 2 + .../billing/shared/update-license-types.ts | 2 + apps/web/src/app/dirt/reports/reports.ts | 2 + .../reports/shared/models/report-variant.ts | 2 + .../vault-item-dialog.component.ts | 2 + .../web-generator-dialog.component.ts | 2 + .../individual-vault/add-edit-v2.component.ts | 2 + .../bulk-delete-dialog.component.ts | 2 + .../bulk-move-dialog.component.ts | 2 + .../folder-add-edit.component.ts | 2 + .../services/vault-banners.service.ts | 2 + .../models/vault-filter-section.type.ts | 2 + .../vault/individual-vault/view.component.ts | 2 + .../browser-extension-prompt.service.ts | 2 + .../risk-insights/models/password-health.ts | 2 + .../add-edit-member-dialog.component.ts | 2 + ...-existing-organization-dialog.component.ts | 2 + .../clients/create-client-dialog.component.ts | 2 + .../manage-client-name-dialog.component.ts | 2 + ...ge-client-subscription-dialog.component.ts | 2 + .../risk-insights.component.ts | 2 + .../dialog/project-dialog.component.ts | 2 + .../secrets/dialog/secret-dialog.component.ts | 4 ++ .../service-account-dialog.component.ts | 2 + .../models/enums/ap-item.enum.ts | 2 + .../models/enums/ap-permission.enum.ts | 2 + .../bulk-confirmation-dialog.component.ts | 2 + eslint.config.mjs | 1 + .../add-account-credit-dialog.component.ts | 2 + .../src/tools/send/add-edit.component.ts | 2 + .../input-password.component.ts | 2 + .../login-decryption-options.component.ts | 2 + .../login-via-auth-request.component.ts | 2 + .../auth/src/angular/login/login.component.ts | 2 + .../registration-start.component.ts | 2 + .../two-factor-auth-component.service.ts | 4 ++ .../active-client-verification-option.enum.ts | 2 + .../validators/compare-inputs.validator.ts | 2 + .../enums/organization-api-key-type.enum.ts | 2 + .../organization-connection-type.enum.ts | 2 + .../organization-user-status-type.enum.ts | 2 + .../enums/organization-user-type.enum.ts | 2 + .../admin-console/enums/policy-type.enum.ts | 2 + .../enums/provider-status-type.enum.ts | 2 + .../admin-console/enums/provider-type.enum.ts | 2 + .../enums/provider-user-status-type.enum.ts | 2 + .../enums/provider-user-type.enum.ts | 2 + .../enums/scim-provider-type.enum.ts | 2 + .../src/auth/enums/auth-request-type.ts | 2 + .../src/auth/enums/authentication-status.ts | 2 + .../src/auth/enums/authentication-type.ts | 2 + libs/common/src/auth/enums/sso.ts | 12 ++++ .../auth/enums/two-factor-provider-type.ts | 2 + .../src/auth/enums/verification-type.ts | 2 + .../domain/force-set-password-reason.ts | 2 + .../common/src/auth/services/token.service.ts | 2 + .../enums/bitwarden-product-type.enum.ts | 2 + .../billing/enums/payment-method-type.enum.ts | 2 + .../src/billing/enums/plan-interval.enum.ts | 2 + .../enums/plan-sponsorship-type.enum.ts | 2 + .../src/billing/enums/plan-type.enum.ts | 2 + .../billing/enums/product-tier-type.enum.ts | 2 + .../src/billing/enums/product-type.enum.ts | 2 + .../billing/enums/transaction-type.enum.ts | 2 + libs/common/src/enums/client-type.enum.ts | 2 + libs/common/src/enums/device-type.enum.ts | 2 + .../src/enums/event-system-user.enum.ts | 2 + libs/common/src/enums/event-type.enum.ts | 2 + libs/common/src/enums/feature-flag.enum.ts | 2 + .../common/src/enums/http-status-code.enum.ts | 2 + .../common/src/enums/integration-type.enum.ts | 2 + .../enums/native-messaging-version.enum.ts | 2 + .../src/enums/notification-type.enum.ts | 2 + libs/common/src/enums/push-technology.enum.ts | 2 + .../enums/vault-timeout-action.enum.ts | 2 + .../abstractions/environment.service.ts | 2 + ...fido2-authenticator.service.abstraction.ts | 4 ++ .../platform/enums/encryption-type.enum.ts | 2 + .../platform/enums/file-upload-type.enum.ts | 2 + .../src/platform/enums/hash-purpose.enum.ts | 2 + .../enums/html-storage-location.enum.ts | 2 + .../platform/enums/key-suffix-options.enum.ts | 2 + .../src/platform/enums/log-level-type.enum.ts | 2 + .../platform/enums/storage-location.enum.ts | 2 + .../src/platform/enums/theme-type.enum.ts | 2 + .../services/cryptography/initializer-key.ts | 2 + ...8-move-provider-state-to-state-provider.ts | 4 ++ .../30-move-policy-state-to-state-provider.ts | 2 + ...ve-organization-state-to-state-provider.ts | 8 +++ .../migrations/54-move-encrypted-sends.ts | 2 + .../59-move-kdf-config-to-state-provider.ts | 2 + ...-timeout-settings-svc-to-state-provider.ts | 2 + libs/common/src/tools/send/enums/send-type.ts | 2 + .../src/vault/enums/cipher-reprompt-type.ts | 2 + libs/common/src/vault/enums/cipher-type.ts | 2 + .../common/src/vault/enums/field-type.enum.ts | 2 + .../src/vault/enums/linked-id-type.enum.ts | 6 ++ .../src/vault/enums/secure-note-type.enum.ts | 2 + .../tasks/enums/security-task-status.enum.ts | 2 + .../tasks/enums/security-task-type.enum.ts | 2 + .../color-password.component.ts | 2 + libs/eslint/platform/index.mjs | 3 +- libs/eslint/platform/no-enums.mjs | 23 ++++++ libs/eslint/platform/no-enums.spec.mjs | 71 +++++++++++++++++++ .../importers/fsecure/fsecure-fsk-types.ts | 2 + .../lastpass/access/enums/idp-provider.ts | 2 + .../access/enums/lastpass-login-type.ts | 2 + .../lastpass/access/enums/otp-method.ts | 2 + .../lastpass/access/enums/platform.ts | 2 + .../types/onepassword-1pux-importer-types.ts | 4 ++ .../protonpass/types/protonpass-json-type.ts | 2 + .../src/biometrics/biometrics-commands.ts | 2 + .../src/biometrics/biometrics-status.ts | 2 + .../key-management/src/enums/kdf-type.enum.ts | 2 + .../src/enums/encrypted-export-type.enum.ts | 2 + .../send-add-edit-dialog.component.ts | 2 + .../send-details/send-details.component.ts | 2 + .../attachments/attachments-v2.component.ts | 2 + .../add-edit-folder-dialog.component.ts | 2 + .../assign-collections.component.ts | 2 + .../src/services/vault-nudges.service.ts | 2 + 156 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 libs/eslint/platform/no-enums.mjs create mode 100644 libs/eslint/platform/no-enums.spec.mjs diff --git a/apps/browser/src/autofill/enums/autofill-overlay.enum.ts b/apps/browser/src/autofill/enums/autofill-overlay.enum.ts index 9cc457f3c1a..d0b970671a8 100644 --- a/apps/browser/src/autofill/enums/autofill-overlay.enum.ts +++ b/apps/browser/src/autofill/enums/autofill-overlay.enum.ts @@ -21,6 +21,8 @@ export const RedirectFocusDirection = { Next: "next", } as const; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum InlineMenuFillType { AccountCreationUsername = 5, PasswordGeneration = 6, diff --git a/apps/browser/src/autofill/fido2/content/messaging/message.ts b/apps/browser/src/autofill/fido2/content/messaging/message.ts index 5815be9eb60..640af22ab9a 100644 --- a/apps/browser/src/autofill/fido2/content/messaging/message.ts +++ b/apps/browser/src/autofill/fido2/content/messaging/message.ts @@ -5,6 +5,8 @@ import { AssertCredentialResult, } from "@bitwarden/common/platform/abstractions/fido2/fido2-client.service.abstraction"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum MessageType { CredentialCreationRequest, CredentialCreationResponse, diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts index 49804abda5d..def425a51a5 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts @@ -26,6 +26,8 @@ import { PopOutComponent } from "../../../platform/popup/components/pop-out.comp import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum SendState { Empty, NoResults, diff --git a/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts b/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts index fcca125c2b6..0133bccd25c 100644 --- a/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts +++ b/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts @@ -10,6 +10,8 @@ import { import { I18nPipe } from "@bitwarden/ui-common"; import { DarkImageSourceDirective, VaultCarouselModule } from "@bitwarden/vault"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AtRiskCarouselDialogResult { Dismissed = "dismissed", } diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts index 137f2a9dac3..4daffa6a9b8 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts @@ -30,6 +30,8 @@ export interface GeneratorDialogResult { generatedValue?: string; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum GeneratorDialogAction { Selected = "selected", Canceled = "canceled", diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts index 64805a02394..4a8625f982c 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts @@ -55,6 +55,8 @@ import { VaultPageService } from "./vault-page.service"; import { AutofillVaultListItemsComponent, VaultListItemsContainerComponent } from "."; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum VaultState { Empty, NoResults, diff --git a/apps/desktop/native-messaging-test-runner/src/ipc.service.ts b/apps/desktop/native-messaging-test-runner/src/ipc.service.ts index b02ff1a4225..d8616e9757a 100644 --- a/apps/desktop/native-messaging-test-runner/src/ipc.service.ts +++ b/apps/desktop/native-messaging-test-runner/src/ipc.service.ts @@ -15,6 +15,8 @@ const DEFAULT_MESSAGE_TIMEOUT = 10 * 1000; // 10 seconds export type MessageHandler = (MessageCommon) => void; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum IPCConnectionState { Disconnected = "disconnected", Connecting = "connecting", diff --git a/apps/desktop/src/app/tools/send/send.component.ts b/apps/desktop/src/app/tools/send/send.component.ts index cc3007ae133..6c2c3ed53c6 100644 --- a/apps/desktop/src/app/tools/send/send.component.ts +++ b/apps/desktop/src/app/tools/send/send.component.ts @@ -25,6 +25,8 @@ import { SearchBarService } from "../../layout/search/search-bar.service"; import { AddEditComponent } from "./add-edit.component"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum Action { None = "", Add = "add", diff --git a/apps/desktop/src/autofill/models/ssh-agent-setting.ts b/apps/desktop/src/autofill/models/ssh-agent-setting.ts index f332cc93ee1..1775cf35588 100644 --- a/apps/desktop/src/autofill/models/ssh-agent-setting.ts +++ b/apps/desktop/src/autofill/models/ssh-agent-setting.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum SshAgentPromptType { Always = "always", Never = "never", diff --git a/apps/desktop/src/types/biometric-message.ts b/apps/desktop/src/types/biometric-message.ts index f7a7ef0c507..7616b265005 100644 --- a/apps/desktop/src/types/biometric-message.ts +++ b/apps/desktop/src/types/biometric-message.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum BiometricAction { Authenticate = "authenticate", GetStatus = "status", diff --git a/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts b/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts index 2858d7330e5..204615443ba 100644 --- a/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts +++ b/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts @@ -31,6 +31,8 @@ export interface CredentialGeneratorDialogResult { generatedValue?: string; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum CredentialGeneratorDialogAction { Selected = "selected", Canceled = "canceled", diff --git a/apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.ts b/apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.ts index dd19c66f21e..147340e6a00 100644 --- a/apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.ts @@ -43,6 +43,8 @@ export interface BulkCollectionsDialogParams { collections: CollectionView[]; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum BulkCollectionsDialogResult { Saved = "saved", Canceled = "canceled", diff --git a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts index 687aef9b671..96c00faceb2 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts @@ -132,6 +132,8 @@ import { VaultHeaderComponent } from "./vault-header/vault-header.component"; const BroadcasterSubscriptionId = "OrgVaultComponent"; const SearchTextDebounceInterval = 200; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum AddAccessStatusType { All = 0, AddAccess = 1, diff --git a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts index 2a5af32ecc2..f29b4b642cb 100644 --- a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts @@ -58,6 +58,8 @@ import { AddEditGroupDetail } from "./../core/views/add-edit-group-detail"; /** * Indices for the available tabs in the dialog */ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum GroupAddEditTabType { Info = 0, Members = 1, @@ -82,6 +84,8 @@ export interface GroupAddEditDialogParams { initialTab?: GroupAddEditTabType; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum GroupAddEditDialogResultType { Saved = "saved", Canceled = "canceled", diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts index c90a2a657e7..8349b44c735 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts @@ -64,6 +64,8 @@ import { commaSeparatedEmails } from "./validators/comma-separated-emails.valida import { inputEmailLimitValidator } from "./validators/input-email-limit.validator"; import { orgSeatLimitReachedValidator } from "./validators/org-seat-limit-reached.validator"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum MemberDialogTab { Role = 0, Groups = 1, @@ -92,6 +94,8 @@ export interface EditMemberDialogParams extends CommonMemberDialogParams { export type MemberDialogParams = EditMemberDialogParams | AddMemberDialogParams; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum MemberDialogResult { Saved = "saved", Canceled = "canceled", diff --git a/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts b/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts index f08cb0b7d7c..4e78d4dc91f 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts @@ -50,6 +50,8 @@ export type ResetPasswordDialogData = { organizationId: string; }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ResetPasswordDialogResult { Ok = "ok", } diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts index 49f4d15a100..4d722840e23 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts @@ -41,6 +41,8 @@ export type PolicyEditDialogData = { organizationId: string; }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum PolicyEditDialogResult { Saved = "saved", UpgradePlan = "upgrade-plan", diff --git a/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts b/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts index c23dcf2c8f2..e942eecbd37 100644 --- a/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts @@ -71,6 +71,8 @@ export interface DeleteOrganizationDialogParams { requestType: "InvalidFamiliesForEnterprise" | "RegularDelete"; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum DeleteOrganizationDialogResult { Deleted = "deleted", Canceled = "canceled", diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts index edd0bfcaada..1db1fc8a06e 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts @@ -26,6 +26,8 @@ import { Permission, } from "./access-selector.models"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum PermissionMode { /** * No permission controls or column present. No permission values are emitted. diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.models.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.models.ts index 8702c0f7a6c..884483d32b0 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.models.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.models.ts @@ -15,6 +15,8 @@ import { GroupView } from "../../../core"; /** * Permission options that replace/correspond with manage, readOnly, and hidePassword server fields. */ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum CollectionPermission { View = "view", ViewExceptPass = "viewExceptPass", @@ -23,6 +25,8 @@ export enum CollectionPermission { Manage = "manage", } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AccessItemType { Collection, Group, diff --git a/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts index 37d0ebbd195..07bff3aba64 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts @@ -65,6 +65,8 @@ import { } from "../access-selector/access-selector.models"; import { AccessSelectorModule } from "../access-selector/access-selector.module"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum CollectionDialogTabType { Info = 0, Access = 1, @@ -76,6 +78,8 @@ export enum CollectionDialogTabType { * @readonly * @enum {string} */ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum ButtonType { /** Displayed when the user has reached the maximum number of collections allowed for the organization. */ Upgrade = "upgrade", @@ -103,6 +107,8 @@ export interface CollectionDialogResult { collection: CollectionResponse | CollectionView; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum CollectionDialogAction { Saved = "saved", Canceled = "canceled", diff --git a/apps/web/src/app/auth/core/enums/webauthn-login-credential-prf-status.enum.ts b/apps/web/src/app/auth/core/enums/webauthn-login-credential-prf-status.enum.ts index 7dc8217fde5..3073917e57b 100644 --- a/apps/web/src/app/auth/core/enums/webauthn-login-credential-prf-status.enum.ts +++ b/apps/web/src/app/auth/core/enums/webauthn-login-credential-prf-status.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum WebauthnLoginCredentialPrfStatus { Enabled = 0, Supported = 1, diff --git a/apps/web/src/app/auth/emergency-access/enums/emergency-access-status-type.ts b/apps/web/src/app/auth/emergency-access/enums/emergency-access-status-type.ts index 94400f34e6e..16aa2546101 100644 --- a/apps/web/src/app/auth/emergency-access/enums/emergency-access-status-type.ts +++ b/apps/web/src/app/auth/emergency-access/enums/emergency-access-status-type.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum EmergencyAccessStatusType { Invited = 0, Accepted = 1, diff --git a/apps/web/src/app/auth/emergency-access/enums/emergency-access-type.ts b/apps/web/src/app/auth/emergency-access/enums/emergency-access-type.ts index 61a366c433e..ecb0c5a3d07 100644 --- a/apps/web/src/app/auth/emergency-access/enums/emergency-access-type.ts +++ b/apps/web/src/app/auth/emergency-access/enums/emergency-access-type.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum EmergencyAccessType { View = 0, Takeover = 1, diff --git a/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.ts b/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.ts index f3fd19a4e8b..95afc167374 100644 --- a/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/confirm/emergency-access-confirm.component.ts @@ -8,6 +8,8 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { DialogConfig, DialogRef, DIALOG_DATA, DialogService } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum EmergencyAccessConfirmDialogResult { Confirmed = "confirmed", } diff --git a/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.ts b/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.ts index cf52969c244..1a6510ef011 100644 --- a/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.ts @@ -26,6 +26,8 @@ export type EmergencyAccessAddEditDialogData = { readOnly: boolean; }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum EmergencyAccessAddEditDialogResult { Saved = "saved", Canceled = "canceled", diff --git a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts index c80f82ae126..edb85dc0f1a 100644 --- a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts @@ -24,6 +24,8 @@ import { KdfType, KdfConfigService, KeyService } from "@bitwarden/key-management import { EmergencyAccessService } from "../../../emergency-access"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum EmergencyAccessTakeoverResultType { Done = "done", } diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.ts b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.ts index 591fe3816dc..8e7e25896ab 100644 --- a/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.ts +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.ts @@ -19,6 +19,8 @@ import { PendingWebauthnLoginCredentialView } from "../../../core/views/pending- import { CreatePasskeyFailedIcon } from "./create-passkey-failed.icon"; import { CreatePasskeyIcon } from "./create-passkey.icon"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum CreateCredentialDialogResult { Success, } diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts index 9f910ad9d23..63c42139648 100644 --- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts @@ -50,11 +50,15 @@ export interface OrganizationCreatedEvent { planDescription: string; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum SubscriptionCadence { Annual, Monthly, } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum SubscriptionProduct { PasswordManager, SecretsManager, diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts index 8cff90edd5b..49c5bb775b1 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts @@ -74,11 +74,15 @@ type ChangePlanDialogParams = { productTierType: ProductTierType; }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ChangePlanDialogResultType { Closed = "closed", Submitted = "submitted", } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum PlanCardState { Selected = "selected", NotSelected = "not_selected", diff --git a/apps/web/src/app/billing/organizations/download-license.component.ts b/apps/web/src/app/billing/organizations/download-license.component.ts index fecb58a7a9d..66778aec50f 100644 --- a/apps/web/src/app/billing/organizations/download-license.component.ts +++ b/apps/web/src/app/billing/organizations/download-license.component.ts @@ -7,6 +7,8 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { DialogConfig, DIALOG_DATA, DialogRef, DialogService } from "@bitwarden/components"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum DownloadLicenseDialogResult { Cancelled = "cancelled", Downloaded = "downloaded", diff --git a/apps/web/src/app/billing/organizations/organization-subscription-selfhost.component.ts b/apps/web/src/app/billing/organizations/organization-subscription-selfhost.component.ts index e6854a5216b..2189bfa830f 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-selfhost.component.ts +++ b/apps/web/src/app/billing/organizations/organization-subscription-selfhost.component.ts @@ -27,6 +27,8 @@ import { DialogService, ToastService } from "@bitwarden/components"; import { BillingSyncKeyComponent } from "./billing-sync-key.component"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum LicenseOptions { SYNC = 0, UPLOAD = 1, diff --git a/apps/web/src/app/billing/shared/add-credit-dialog.component.ts b/apps/web/src/app/billing/shared/add-credit-dialog.component.ts index 45dab542ce8..ec6e251418b 100644 --- a/apps/web/src/app/billing/shared/add-credit-dialog.component.ts +++ b/apps/web/src/app/billing/shared/add-credit-dialog.component.ts @@ -21,6 +21,8 @@ export interface AddCreditDialogData { organizationId: string; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AddCreditDialogResult { Added = "added", Cancelled = "cancelled", diff --git a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts index e7b7cc78250..9d32becd1bb 100644 --- a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts +++ b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts @@ -30,6 +30,8 @@ export interface AdjustPaymentDialogParams { providerId?: string; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AdjustPaymentDialogResultType { Closed = "closed", Submitted = "submitted", diff --git a/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.ts b/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.ts index 3acba414df4..6cd17218f02 100644 --- a/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.ts +++ b/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.ts @@ -22,6 +22,8 @@ export interface AdjustStorageDialogParams { organizationId?: string; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AdjustStorageDialogResultType { Submitted = "submitted", Closed = "closed", diff --git a/apps/web/src/app/billing/shared/offboarding-survey.component.ts b/apps/web/src/app/billing/shared/offboarding-survey.component.ts index cecbc302f40..62213c1fe94 100644 --- a/apps/web/src/app/billing/shared/offboarding-survey.component.ts +++ b/apps/web/src/app/billing/shared/offboarding-survey.component.ts @@ -25,6 +25,8 @@ type OrganizationOffboardingParams = { export type OffboardingSurveyDialogParams = UserOffboardingParams | OrganizationOffboardingParams; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum OffboardingSurveyDialogResultType { Closed = "closed", Submitted = "submitted", diff --git a/apps/web/src/app/billing/shared/update-license-types.ts b/apps/web/src/app/billing/shared/update-license-types.ts index 8f939ac62a9..8ba13541ba8 100644 --- a/apps/web/src/app/billing/shared/update-license-types.ts +++ b/apps/web/src/app/billing/shared/update-license-types.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum UpdateLicenseDialogResult { Updated = "updated", Cancelled = "cancelled", diff --git a/apps/web/src/app/dirt/reports/reports.ts b/apps/web/src/app/dirt/reports/reports.ts index 500ae23e5cf..c47928af1e9 100644 --- a/apps/web/src/app/dirt/reports/reports.ts +++ b/apps/web/src/app/dirt/reports/reports.ts @@ -7,6 +7,8 @@ import { ReportUnsecuredWebsites } from "./icons/report-unsecured-websites.icon" import { ReportWeakPasswords } from "./icons/report-weak-passwords.icon"; import { ReportEntry } from "./shared"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ReportType { ExposedPasswords = "exposedPasswords", ReusedPasswords = "reusedPasswords", diff --git a/apps/web/src/app/dirt/reports/shared/models/report-variant.ts b/apps/web/src/app/dirt/reports/shared/models/report-variant.ts index 3beba65f7d9..48b213f4cf6 100644 --- a/apps/web/src/app/dirt/reports/shared/models/report-variant.ts +++ b/apps/web/src/app/dirt/reports/shared/models/report-variant.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ReportVariant { Enabled = "Enabled", RequiresPremium = "RequiresPremium", diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts index 460b8d58d63..10c35f861b9 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts @@ -95,6 +95,8 @@ export interface VaultItemDialogParams { restore?: (c: CipherView) => Promise; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum VaultItemDialogResult { /** * A cipher was saved (created or updated). diff --git a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts index 2d0aa0231f1..e20efa9dbb8 100644 --- a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts +++ b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts @@ -26,6 +26,8 @@ export interface WebVaultGeneratorDialogResult { generatedValue?: string; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum WebVaultGeneratorDialogAction { Selected = "selected", Canceled = "canceled", diff --git a/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts b/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts index 5dcbf0d4e78..621e0ec88c5 100644 --- a/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts +++ b/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts @@ -35,6 +35,8 @@ import { WebCipherFormGenerationService } from "../services/web-cipher-form-gene /** * The result of the AddEditCipherDialogV2 component. */ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AddEditCipherDialogResult { Edited = "edited", Added = "added", diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts index f8084b03e33..1650b0f371f 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts @@ -29,6 +29,8 @@ export interface BulkDeleteDialogParams { unassignedCiphers?: string[]; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum BulkDeleteDialogResult { Deleted = "deleted", Canceled = "canceled", diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts index 8f0827e4b51..d287c430d49 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts @@ -23,6 +23,8 @@ export interface BulkMoveDialogParams { cipherIds?: string[]; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum BulkMoveDialogResult { Moved = "moved", Canceled = "canceled", diff --git a/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts b/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts index ad6cbfad43d..6a3c5663d93 100644 --- a/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts +++ b/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts @@ -113,6 +113,8 @@ export interface FolderAddEditDialogParams { folderId: string; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum FolderAddEditDialogResult { Deleted = "deleted", Canceled = "canceled", diff --git a/apps/web/src/app/vault/individual-vault/vault-banners/services/vault-banners.service.ts b/apps/web/src/app/vault/individual-vault/vault-banners/services/vault-banners.service.ts index 1fa5ae1ad8b..ca16541f88f 100644 --- a/apps/web/src/app/vault/individual-vault/vault-banners/services/vault-banners.service.ts +++ b/apps/web/src/app/vault/individual-vault/vault-banners/services/vault-banners.service.ts @@ -17,6 +17,8 @@ import { UserId } from "@bitwarden/common/types/guid"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { PBKDF2KdfConfig, KdfConfigService, KdfType } from "@bitwarden/key-management"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum VisibleVaultBanner { KDFSettings = "kdf-settings", OutdatedBrowser = "outdated-browser", diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts index 0f949e17146..7566dbdc507 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts @@ -15,6 +15,8 @@ export type VaultFilterType = | FolderFilter | CollectionFilter; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum VaultFilterLabel { OrganizationFilter = "organizationFilter", TypeFilter = "typeFilter", diff --git a/apps/web/src/app/vault/individual-vault/view.component.ts b/apps/web/src/app/vault/individual-vault/view.component.ts index e7b06cbb8d6..f52a4da3ffb 100644 --- a/apps/web/src/app/vault/individual-vault/view.component.ts +++ b/apps/web/src/app/vault/individual-vault/view.component.ts @@ -54,6 +54,8 @@ export interface ViewCipherDialogParams { disableEdit?: boolean; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ViewCipherDialogResult { Edited = "edited", Deleted = "deleted", diff --git a/apps/web/src/app/vault/services/browser-extension-prompt.service.ts b/apps/web/src/app/vault/services/browser-extension-prompt.service.ts index 5617d4aef75..f928404a2a9 100644 --- a/apps/web/src/app/vault/services/browser-extension-prompt.service.ts +++ b/apps/web/src/app/vault/services/browser-extension-prompt.service.ts @@ -7,6 +7,8 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { Utils } from "@bitwarden/common/platform/misc/utils"; import { VaultMessages } from "@bitwarden/common/vault/enums/vault-messages.enum"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum BrowserPromptState { Loading = "loading", Error = "error", diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/password-health.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/password-health.ts index 723d737d5bd..b8333828693 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/password-health.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/password-health.ts @@ -149,6 +149,8 @@ export interface PasswordHealthReportApplicationsRequest { url: string; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum DrawerType { None = 0, AppAtRiskMembers = 1, diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/add-edit-member-dialog.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/add-edit-member-dialog.component.ts index 0213903c4d9..67f2382cf91 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/add-edit-member-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/add-edit-member-dialog.component.ts @@ -25,6 +25,8 @@ export type AddEditMemberDialogParams = { }; }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AddEditMemberDialogResultType { Closed = "closed", Deleted = "deleted", diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/add-existing-organization-dialog.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/add-existing-organization-dialog.component.ts index 0d1602946c7..4bb2c36ef15 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/add-existing-organization-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/add-existing-organization-dialog.component.ts @@ -18,6 +18,8 @@ export type AddExistingOrganizationDialogParams = { provider: Provider; }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AddExistingOrganizationDialogResultType { Closed = "closed", Submitted = "submitted", diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.ts index d3245916ad7..d71e18cd539 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.ts @@ -22,6 +22,8 @@ type CreateClientDialogParams = { plans: PlanResponse[]; }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum CreateClientDialogResultType { Closed = "closed", Submitted = "submitted", diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-client-name-dialog.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-client-name-dialog.component.ts index bbcd5f7ed63..45abeab1f4a 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-client-name-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-client-name-dialog.component.ts @@ -23,6 +23,8 @@ type ManageClientNameDialogParams = { }; }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ManageClientNameDialogResultType { Closed = "closed", Submitted = "submitted", diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-client-subscription-dialog.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-client-subscription-dialog.component.ts index b79c887e77e..ced48bfdbea 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-client-subscription-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-client-subscription-dialog.component.ts @@ -18,6 +18,8 @@ type ManageClientSubscriptionDialogParams = { provider: Provider; }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ManageClientSubscriptionDialogResultType { Closed = "closed", Submitted = "submitted", diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts index ab74869bfc9..5aca124a46a 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts @@ -37,6 +37,8 @@ import { PasswordHealthMembersURIComponent } from "./password-health-members-uri import { PasswordHealthMembersComponent } from "./password-health-members.component"; import { PasswordHealthComponent } from "./password-health.component"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum RiskInsightsTabType { AllApps = 0, CriticalApps = 1, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts index 74824de00e0..c96887cc9ac 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts @@ -11,6 +11,8 @@ import { DialogRef, DIALOG_DATA, BitValidators, ToastService } from "@bitwarden/ import { ProjectView } from "../../models/view/project.view"; import { ProjectService } from "../../projects/project.service"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum OperationType { Add, Edit, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts index 88b3e5e2172..09a78e02c44 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts @@ -44,11 +44,15 @@ import { SecretService } from "../secret.service"; import { SecretDeleteDialogComponent, SecretDeleteOperation } from "./secret-delete.component"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum OperationType { Add, Edit, } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum SecretDialogTabType { NameValuePair = 0, People = 1, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts index e1cd7a77b28..241c736fb7a 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts @@ -10,6 +10,8 @@ import { DialogRef, DIALOG_DATA, BitValidators, ToastService } from "@bitwarden/ import { ServiceAccountView } from "../../models/view/service-account.view"; import { ServiceAccountService } from "../service-account.service"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum OperationType { Add, Edit, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-item.enum.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-item.enum.ts index 6d060ac255d..6b92fd7458a 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-item.enum.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-item.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ApItemEnum { User, Group, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-permission.enum.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-permission.enum.ts index eb442b0af5d..a57f9636178 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-permission.enum.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-permission.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ApPermissionEnum { CanRead = "canRead", CanReadWrite = "canReadWrite", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/dialogs/bulk-confirmation-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/dialogs/bulk-confirmation-dialog.component.ts index a43207ed75e..935ee1c8518 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/dialogs/bulk-confirmation-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/dialogs/bulk-confirmation-dialog.component.ts @@ -15,6 +15,8 @@ export interface BulkConfirmationStatus { description: string; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum BulkConfirmationResult { Continue, Cancel, diff --git a/eslint.config.mjs b/eslint.config.mjs index 9d93d1118c0..7928224dc00 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -73,6 +73,7 @@ export default tseslint.config( "@angular-eslint/use-lifecycle-interface": "error", "@angular-eslint/use-pipe-transform-interface": 0, "@bitwarden/platform/required-using": "error", + "@bitwarden/platform/no-enums": "error", "@typescript-eslint/explicit-member-accessibility": ["error", { accessibility: "no-public" }], "@typescript-eslint/no-explicit-any": "off", // TODO: This should be re-enabled "@typescript-eslint/no-floating-promises": "error", diff --git a/libs/angular/src/billing/components/add-account-credit-dialog/add-account-credit-dialog.component.ts b/libs/angular/src/billing/components/add-account-credit-dialog/add-account-credit-dialog.component.ts index 7c068c51d86..871895c2ede 100644 --- a/libs/angular/src/billing/components/add-account-credit-dialog/add-account-credit-dialog.component.ts +++ b/libs/angular/src/billing/components/add-account-credit-dialog/add-account-credit-dialog.component.ts @@ -22,6 +22,8 @@ export type AddAccountCreditDialogParams = { providerId?: string; }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AddAccountCreditDialogResultType { Closed = "closed", Submitted = "submitted", diff --git a/libs/angular/src/tools/send/add-edit.component.ts b/libs/angular/src/tools/send/add-edit.component.ts index 7e6180e5849..0289664c365 100644 --- a/libs/angular/src/tools/send/add-edit.component.ts +++ b/libs/angular/src/tools/send/add-edit.component.ts @@ -36,6 +36,8 @@ import { SendService } from "@bitwarden/common/tools/send/services/send.service. import { DialogService, ToastService } from "@bitwarden/components"; // Value = hours +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum DatePreset { OneHour = 1, OneDay = 24, diff --git a/libs/auth/src/angular/input-password/input-password.component.ts b/libs/auth/src/angular/input-password/input-password.component.ts index bffb8a56b5e..dff4eafc3c2 100644 --- a/libs/auth/src/angular/input-password/input-password.component.ts +++ b/libs/auth/src/angular/input-password/input-password.component.ts @@ -36,6 +36,8 @@ import { PasswordInputResult } from "./password-input-result"; /** * Determines which form input elements will be displayed in the UI. */ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum InputPasswordFlow { /** * - Input: New password diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index 945e6bbaaf5..3ea9416b7e2 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -41,6 +41,8 @@ import { AnonLayoutWrapperDataService } from "../anon-layout/anon-layout-wrapper import { LoginDecryptionOptionsService } from "./login-decryption-options.service"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum State { NewUser, ExistingUserUntrustedDevice, diff --git a/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts b/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts index 4c95a1eca3e..d74deb443f5 100644 --- a/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts +++ b/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts @@ -40,6 +40,8 @@ import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legac import { AuthRequestApiService } from "../../common/abstractions/auth-request-api.service"; import { LoginViaAuthRequestCacheService } from "../../common/services/auth-request/default-login-via-auth-request-cache.service"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum Flow { StandardAuthRequest, // when user clicks "Login with device" from /login or "Approve from your other device" from /login-initiated AdminAuthRequest, // when user clicks "Request admin approval" from /login-initiated diff --git a/libs/auth/src/angular/login/login.component.ts b/libs/auth/src/angular/login/login.component.ts index cd226cddcec..d5180f56785 100644 --- a/libs/auth/src/angular/login/login.component.ts +++ b/libs/auth/src/angular/login/login.component.ts @@ -46,6 +46,8 @@ import { LoginComponentService, PasswordPolicies } from "./login-component.servi const BroadcasterSubscriptionId = "LoginComponent"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum LoginUiState { EMAIL_ENTRY = "EmailEntry", MASTER_PASSWORD_ENTRY = "MasterPasswordEntry", diff --git a/libs/auth/src/angular/registration/registration-start/registration-start.component.ts b/libs/auth/src/angular/registration/registration-start/registration-start.component.ts index e365ff09aa2..44d1d720a8d 100644 --- a/libs/auth/src/angular/registration/registration-start/registration-start.component.ts +++ b/libs/auth/src/angular/registration/registration-start/registration-start.component.ts @@ -26,6 +26,8 @@ import { RegistrationUserAddIcon } from "../../icons"; import { RegistrationCheckEmailIcon } from "../../icons/registration-check-email.icon"; import { RegistrationEnvSelectorComponent } from "../registration-env-selector/registration-env-selector.component"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum RegistrationStartState { USER_DATA_ENTRY = "UserDataEntry", CHECK_EMAIL = "CheckEmail", diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth-component.service.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth-component.service.ts index 2bb354a8cc3..c99722fb8e4 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth-component.service.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth-component.service.ts @@ -1,10 +1,14 @@ import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum LegacyKeyMigrationAction { PREVENT_LOGIN_AND_SHOW_REQUIRE_MIGRATION_WARNING, NAVIGATE_TO_MIGRATION_COMPONENT, } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum DuoLaunchAction { DIRECT_LAUNCH, SINGLE_ACTION_POPOUT, diff --git a/libs/auth/src/angular/user-verification/active-client-verification-option.enum.ts b/libs/auth/src/angular/user-verification/active-client-verification-option.enum.ts index bceccc7f965..ef2bd1855c6 100644 --- a/libs/auth/src/angular/user-verification/active-client-verification-option.enum.ts +++ b/libs/auth/src/angular/user-verification/active-client-verification-option.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ActiveClientVerificationOption { MasterPassword = "masterPassword", Pin = "pin", diff --git a/libs/auth/src/angular/validators/compare-inputs.validator.ts b/libs/auth/src/angular/validators/compare-inputs.validator.ts index 24568ade0e3..79d547859a4 100644 --- a/libs/auth/src/angular/validators/compare-inputs.validator.ts +++ b/libs/auth/src/angular/validators/compare-inputs.validator.ts @@ -1,5 +1,7 @@ import { AbstractControl, FormGroup, ValidationErrors, ValidatorFn } from "@angular/forms"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ValidationGoal { InputsShouldMatch, InputsShouldNotMatch, diff --git a/libs/common/src/admin-console/enums/organization-api-key-type.enum.ts b/libs/common/src/admin-console/enums/organization-api-key-type.enum.ts index 44ba7f8391d..bb98ea8718b 100644 --- a/libs/common/src/admin-console/enums/organization-api-key-type.enum.ts +++ b/libs/common/src/admin-console/enums/organization-api-key-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum OrganizationApiKeyType { Default = 0, BillingSync = 1, diff --git a/libs/common/src/admin-console/enums/organization-connection-type.enum.ts b/libs/common/src/admin-console/enums/organization-connection-type.enum.ts index d2f9700a6a0..3cd11a29496 100644 --- a/libs/common/src/admin-console/enums/organization-connection-type.enum.ts +++ b/libs/common/src/admin-console/enums/organization-connection-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum OrganizationConnectionType { CloudBillingSync = 1, Scim = 2, diff --git a/libs/common/src/admin-console/enums/organization-user-status-type.enum.ts b/libs/common/src/admin-console/enums/organization-user-status-type.enum.ts index f5fa0e25c91..df9ccc8b430 100644 --- a/libs/common/src/admin-console/enums/organization-user-status-type.enum.ts +++ b/libs/common/src/admin-console/enums/organization-user-status-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum OrganizationUserStatusType { Invited = 0, Accepted = 1, diff --git a/libs/common/src/admin-console/enums/organization-user-type.enum.ts b/libs/common/src/admin-console/enums/organization-user-type.enum.ts index da50bfbdc20..098374becb4 100644 --- a/libs/common/src/admin-console/enums/organization-user-type.enum.ts +++ b/libs/common/src/admin-console/enums/organization-user-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum OrganizationUserType { Owner = 0, Admin = 1, diff --git a/libs/common/src/admin-console/enums/policy-type.enum.ts b/libs/common/src/admin-console/enums/policy-type.enum.ts index 336b834ca56..42ab798eabf 100644 --- a/libs/common/src/admin-console/enums/policy-type.enum.ts +++ b/libs/common/src/admin-console/enums/policy-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum PolicyType { TwoFactorAuthentication = 0, // Requires users to have 2fa enabled MasterPassword = 1, // Sets minimum requirements for master password complexity diff --git a/libs/common/src/admin-console/enums/provider-status-type.enum.ts b/libs/common/src/admin-console/enums/provider-status-type.enum.ts index 8da60af0eb3..32b730510e0 100644 --- a/libs/common/src/admin-console/enums/provider-status-type.enum.ts +++ b/libs/common/src/admin-console/enums/provider-status-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ProviderStatusType { Pending = 0, Created = 1, diff --git a/libs/common/src/admin-console/enums/provider-type.enum.ts b/libs/common/src/admin-console/enums/provider-type.enum.ts index eb48e362e7d..dbb60fb3638 100644 --- a/libs/common/src/admin-console/enums/provider-type.enum.ts +++ b/libs/common/src/admin-console/enums/provider-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ProviderType { Msp = 0, Reseller = 1, diff --git a/libs/common/src/admin-console/enums/provider-user-status-type.enum.ts b/libs/common/src/admin-console/enums/provider-user-status-type.enum.ts index 38d6c2e4961..253128aa25f 100644 --- a/libs/common/src/admin-console/enums/provider-user-status-type.enum.ts +++ b/libs/common/src/admin-console/enums/provider-user-status-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ProviderUserStatusType { Invited = 0, Accepted = 1, diff --git a/libs/common/src/admin-console/enums/provider-user-type.enum.ts b/libs/common/src/admin-console/enums/provider-user-type.enum.ts index 00490adcfca..d20755c3196 100644 --- a/libs/common/src/admin-console/enums/provider-user-type.enum.ts +++ b/libs/common/src/admin-console/enums/provider-user-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ProviderUserType { ProviderAdmin = 0, ServiceUser = 1, diff --git a/libs/common/src/admin-console/enums/scim-provider-type.enum.ts b/libs/common/src/admin-console/enums/scim-provider-type.enum.ts index 43c518fdfbf..cd33a1cf891 100644 --- a/libs/common/src/admin-console/enums/scim-provider-type.enum.ts +++ b/libs/common/src/admin-console/enums/scim-provider-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ScimProviderType { Default = 0, AzureAd = 1, diff --git a/libs/common/src/auth/enums/auth-request-type.ts b/libs/common/src/auth/enums/auth-request-type.ts index 31db2467861..d23fec03205 100644 --- a/libs/common/src/auth/enums/auth-request-type.ts +++ b/libs/common/src/auth/enums/auth-request-type.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AuthRequestType { AuthenticateAndUnlock = 0, Unlock = 1, diff --git a/libs/common/src/auth/enums/authentication-status.ts b/libs/common/src/auth/enums/authentication-status.ts index 17b4f1f21e8..6a6f9467ae7 100644 --- a/libs/common/src/auth/enums/authentication-status.ts +++ b/libs/common/src/auth/enums/authentication-status.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AuthenticationStatus { LoggedOut = 0, Locked = 1, diff --git a/libs/common/src/auth/enums/authentication-type.ts b/libs/common/src/auth/enums/authentication-type.ts index 35b50e6400a..13a3d70ddda 100644 --- a/libs/common/src/auth/enums/authentication-type.ts +++ b/libs/common/src/auth/enums/authentication-type.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AuthenticationType { Password = 0, Sso = 1, diff --git a/libs/common/src/auth/enums/sso.ts b/libs/common/src/auth/enums/sso.ts index 0c86a27151f..1e5766c7afd 100644 --- a/libs/common/src/auth/enums/sso.ts +++ b/libs/common/src/auth/enums/sso.ts @@ -1,25 +1,35 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum SsoType { None = 0, OpenIdConnect = 1, Saml2 = 2, } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum MemberDecryptionType { MasterPassword = 0, KeyConnector = 1, TrustedDeviceEncryption = 2, } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum OpenIdConnectRedirectBehavior { RedirectGet = 0, FormPost = 1, } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum Saml2BindingType { HttpRedirect = 1, HttpPost = 2, } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum Saml2NameIdFormat { NotConfigured = 0, Unspecified = 1, @@ -32,6 +42,8 @@ export enum Saml2NameIdFormat { Transient = 8, } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum Saml2SigningBehavior { IfIdpWantAuthnRequestsSigned = 0, Always = 1, diff --git a/libs/common/src/auth/enums/two-factor-provider-type.ts b/libs/common/src/auth/enums/two-factor-provider-type.ts index b3308b6c12f..9be22bf90c8 100644 --- a/libs/common/src/auth/enums/two-factor-provider-type.ts +++ b/libs/common/src/auth/enums/two-factor-provider-type.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum TwoFactorProviderType { Authenticator = 0, Email = 1, diff --git a/libs/common/src/auth/enums/verification-type.ts b/libs/common/src/auth/enums/verification-type.ts index c1991162f91..f0ad4c0d19c 100644 --- a/libs/common/src/auth/enums/verification-type.ts +++ b/libs/common/src/auth/enums/verification-type.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum VerificationType { MasterPassword = 0, OTP = 1, diff --git a/libs/common/src/auth/models/domain/force-set-password-reason.ts b/libs/common/src/auth/models/domain/force-set-password-reason.ts index 56d52860443..68392125281 100644 --- a/libs/common/src/auth/models/domain/force-set-password-reason.ts +++ b/libs/common/src/auth/models/domain/force-set-password-reason.ts @@ -2,6 +2,8 @@ * This enum is used to determine if a user should be forced to initially set or reset their password * on login (server flag) or unlock via MP (client evaluation). */ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ForceSetPasswordReason { /** * A password reset should not be forced. diff --git a/libs/common/src/auth/services/token.service.ts b/libs/common/src/auth/services/token.service.ts index c27afa6805a..61c00f69215 100644 --- a/libs/common/src/auth/services/token.service.ts +++ b/libs/common/src/auth/services/token.service.ts @@ -43,6 +43,8 @@ import { SECURITY_STAMP_MEMORY, } from "./token.state"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum TokenStorageLocation { Disk = "disk", SecureStorage = "secureStorage", diff --git a/libs/common/src/billing/enums/bitwarden-product-type.enum.ts b/libs/common/src/billing/enums/bitwarden-product-type.enum.ts index 76b0899fd9c..4389d283c00 100644 --- a/libs/common/src/billing/enums/bitwarden-product-type.enum.ts +++ b/libs/common/src/billing/enums/bitwarden-product-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum BitwardenProductType { PasswordManager = 0, SecretsManager = 1, diff --git a/libs/common/src/billing/enums/payment-method-type.enum.ts b/libs/common/src/billing/enums/payment-method-type.enum.ts index 9b29263175c..6ea98fdc5ec 100644 --- a/libs/common/src/billing/enums/payment-method-type.enum.ts +++ b/libs/common/src/billing/enums/payment-method-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum PaymentMethodType { Card = 0, BankAccount = 1, diff --git a/libs/common/src/billing/enums/plan-interval.enum.ts b/libs/common/src/billing/enums/plan-interval.enum.ts index 546336748cd..c048160310b 100644 --- a/libs/common/src/billing/enums/plan-interval.enum.ts +++ b/libs/common/src/billing/enums/plan-interval.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum PlanInterval { Monthly = 0, Annually = 1, diff --git a/libs/common/src/billing/enums/plan-sponsorship-type.enum.ts b/libs/common/src/billing/enums/plan-sponsorship-type.enum.ts index 3b4c00467c2..845b698f509 100644 --- a/libs/common/src/billing/enums/plan-sponsorship-type.enum.ts +++ b/libs/common/src/billing/enums/plan-sponsorship-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum PlanSponsorshipType { FamiliesForEnterprise = 0, } diff --git a/libs/common/src/billing/enums/plan-type.enum.ts b/libs/common/src/billing/enums/plan-type.enum.ts index c8977703454..5c356ce42fe 100644 --- a/libs/common/src/billing/enums/plan-type.enum.ts +++ b/libs/common/src/billing/enums/plan-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum PlanType { Free = 0, FamiliesAnnually2019 = 1, diff --git a/libs/common/src/billing/enums/product-tier-type.enum.ts b/libs/common/src/billing/enums/product-tier-type.enum.ts index fadf57ccdc5..e94243de588 100644 --- a/libs/common/src/billing/enums/product-tier-type.enum.ts +++ b/libs/common/src/billing/enums/product-tier-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ProductTierType { Free = 0, Families = 1, diff --git a/libs/common/src/billing/enums/product-type.enum.ts b/libs/common/src/billing/enums/product-type.enum.ts index 3072ad0f96a..0c11eb92c79 100644 --- a/libs/common/src/billing/enums/product-type.enum.ts +++ b/libs/common/src/billing/enums/product-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ProductType { PasswordManager = 0, SecretsManager = 1, diff --git a/libs/common/src/billing/enums/transaction-type.enum.ts b/libs/common/src/billing/enums/transaction-type.enum.ts index 34731a2ec33..d4c48253a39 100644 --- a/libs/common/src/billing/enums/transaction-type.enum.ts +++ b/libs/common/src/billing/enums/transaction-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum TransactionType { Charge = 0, Credit = 1, diff --git a/libs/common/src/enums/client-type.enum.ts b/libs/common/src/enums/client-type.enum.ts index 54653f74462..25e9d6f3371 100644 --- a/libs/common/src/enums/client-type.enum.ts +++ b/libs/common/src/enums/client-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ClientType { Web = "web", Browser = "browser", diff --git a/libs/common/src/enums/device-type.enum.ts b/libs/common/src/enums/device-type.enum.ts index ff6329b9ac4..d5628536ff7 100644 --- a/libs/common/src/enums/device-type.enum.ts +++ b/libs/common/src/enums/device-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum DeviceType { Android = 0, iOS = 1, diff --git a/libs/common/src/enums/event-system-user.enum.ts b/libs/common/src/enums/event-system-user.enum.ts index e2d43a4f113..f4abbb1e3e9 100644 --- a/libs/common/src/enums/event-system-user.enum.ts +++ b/libs/common/src/enums/event-system-user.enum.ts @@ -1,4 +1,6 @@ // Note: the enum key is used to describe the EventSystemUser in the UI. Be careful about changing it. +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum EventSystemUser { SCIM = 1, DomainVerification = 2, diff --git a/libs/common/src/enums/event-type.enum.ts b/libs/common/src/enums/event-type.enum.ts index 51b324bb434..9efe49b1b0f 100644 --- a/libs/common/src/enums/event-type.enum.ts +++ b/libs/common/src/enums/event-type.enum.ts @@ -1,4 +1,6 @@ // Increment by 100 for each new set of events +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum EventType { User_LoggedIn = 1000, User_ChangedPassword = 1001, diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index a7679a6248a..ddc75eb0d66 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -7,6 +7,8 @@ import { ServerConfig } from "../platform/abstractions/config/server-config"; * * Flags should be grouped by team to have visibility of ownership and cleanup. */ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum FeatureFlag { /* Admin Console Team */ VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint", diff --git a/libs/common/src/enums/http-status-code.enum.ts b/libs/common/src/enums/http-status-code.enum.ts index 94226c82552..d2dba5d8d4c 100644 --- a/libs/common/src/enums/http-status-code.enum.ts +++ b/libs/common/src/enums/http-status-code.enum.ts @@ -4,6 +4,8 @@ * @see {@link https://en.wikipedia.org/wiki/List_of_HTTP_status_codes} * src: https://gist.github.com/RWOverdijk/6cef816cfdf5722228e01cc05fd4b094 */ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum HttpStatusCode { /** * The server has received the request headers and the client should proceed to send the request body diff --git a/libs/common/src/enums/integration-type.enum.ts b/libs/common/src/enums/integration-type.enum.ts index 42c385fe715..ac8bb9c6afa 100644 --- a/libs/common/src/enums/integration-type.enum.ts +++ b/libs/common/src/enums/integration-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum IntegrationType { Integration = "integration", SDK = "sdk", diff --git a/libs/common/src/enums/native-messaging-version.enum.ts b/libs/common/src/enums/native-messaging-version.enum.ts index f7cf411a40a..f83ec8f4b25 100644 --- a/libs/common/src/enums/native-messaging-version.enum.ts +++ b/libs/common/src/enums/native-messaging-version.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum NativeMessagingVersion { One = 1, // Original implementation Latest = One, diff --git a/libs/common/src/enums/notification-type.enum.ts b/libs/common/src/enums/notification-type.enum.ts index 0e4d0bfee3d..6d731253ce3 100644 --- a/libs/common/src/enums/notification-type.enum.ts +++ b/libs/common/src/enums/notification-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum NotificationType { SyncCipherUpdate = 0, SyncCipherCreate = 1, diff --git a/libs/common/src/enums/push-technology.enum.ts b/libs/common/src/enums/push-technology.enum.ts index 9452c144bb7..1bc4e62cc9d 100644 --- a/libs/common/src/enums/push-technology.enum.ts +++ b/libs/common/src/enums/push-technology.enum.ts @@ -1,6 +1,8 @@ /** * The preferred push technology of the server. */ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum PushTechnology { /** * Indicates that we should use SignalR over web sockets to receive push notifications from the server. diff --git a/libs/common/src/key-management/vault-timeout/enums/vault-timeout-action.enum.ts b/libs/common/src/key-management/vault-timeout/enums/vault-timeout-action.enum.ts index 239a7490191..9e70b7ebb12 100644 --- a/libs/common/src/key-management/vault-timeout/enums/vault-timeout-action.enum.ts +++ b/libs/common/src/key-management/vault-timeout/enums/vault-timeout-action.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum VaultTimeoutAction { Lock = "lock", LogOut = "logOut", diff --git a/libs/common/src/platform/abstractions/environment.service.ts b/libs/common/src/platform/abstractions/environment.service.ts index 4a10f856893..b8931656848 100644 --- a/libs/common/src/platform/abstractions/environment.service.ts +++ b/libs/common/src/platform/abstractions/environment.service.ts @@ -17,6 +17,8 @@ export type Urls = { /** * A subset of available regions, additional regions can be loaded through configuration. */ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum Region { US = "US", EU = "EU", diff --git a/libs/common/src/platform/abstractions/fido2/fido2-authenticator.service.abstraction.ts b/libs/common/src/platform/abstractions/fido2/fido2-authenticator.service.abstraction.ts index e9e68ca92c3..15655393362 100644 --- a/libs/common/src/platform/abstractions/fido2/fido2-authenticator.service.abstraction.ts +++ b/libs/common/src/platform/abstractions/fido2/fido2-authenticator.service.abstraction.ts @@ -46,11 +46,15 @@ export abstract class Fido2AuthenticatorService { silentCredentialDiscovery: (rpId: string) => Promise; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum Fido2AlgorithmIdentifier { ES256 = -7, RS256 = -257, } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum Fido2AuthenticatorErrorCode { Unknown = "UnknownError", NotSupported = "NotSupportedError", diff --git a/libs/common/src/platform/enums/encryption-type.enum.ts b/libs/common/src/platform/enums/encryption-type.enum.ts index fd484dc2fdf..7f4b5048a45 100644 --- a/libs/common/src/platform/enums/encryption-type.enum.ts +++ b/libs/common/src/platform/enums/encryption-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum EncryptionType { AesCbc256_B64 = 0, // Type 1 was the unused and removed AesCbc128_HmacSha256_B64 diff --git a/libs/common/src/platform/enums/file-upload-type.enum.ts b/libs/common/src/platform/enums/file-upload-type.enum.ts index 4cf654fd0ff..a94797b9ef3 100644 --- a/libs/common/src/platform/enums/file-upload-type.enum.ts +++ b/libs/common/src/platform/enums/file-upload-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum FileUploadType { Direct = 0, Azure = 1, diff --git a/libs/common/src/platform/enums/hash-purpose.enum.ts b/libs/common/src/platform/enums/hash-purpose.enum.ts index 887931b9edd..4b61db914a1 100644 --- a/libs/common/src/platform/enums/hash-purpose.enum.ts +++ b/libs/common/src/platform/enums/hash-purpose.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum HashPurpose { ServerAuthorization = 1, LocalAuthorization = 2, diff --git a/libs/common/src/platform/enums/html-storage-location.enum.ts b/libs/common/src/platform/enums/html-storage-location.enum.ts index 19c84fe88c7..1d018a72869 100644 --- a/libs/common/src/platform/enums/html-storage-location.enum.ts +++ b/libs/common/src/platform/enums/html-storage-location.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum HtmlStorageLocation { Local = "local", Memory = "memory", diff --git a/libs/common/src/platform/enums/key-suffix-options.enum.ts b/libs/common/src/platform/enums/key-suffix-options.enum.ts index 98fa215be6a..7cc412f563b 100644 --- a/libs/common/src/platform/enums/key-suffix-options.enum.ts +++ b/libs/common/src/platform/enums/key-suffix-options.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum KeySuffixOptions { Auto = "auto", Pin = "pin", diff --git a/libs/common/src/platform/enums/log-level-type.enum.ts b/libs/common/src/platform/enums/log-level-type.enum.ts index 709871dd5e9..b5f84467d6e 100644 --- a/libs/common/src/platform/enums/log-level-type.enum.ts +++ b/libs/common/src/platform/enums/log-level-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum LogLevelType { Debug, Info, diff --git a/libs/common/src/platform/enums/storage-location.enum.ts b/libs/common/src/platform/enums/storage-location.enum.ts index 46d50d1d38d..9f6e22babec 100644 --- a/libs/common/src/platform/enums/storage-location.enum.ts +++ b/libs/common/src/platform/enums/storage-location.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum StorageLocation { Both = "both", Disk = "disk", diff --git a/libs/common/src/platform/enums/theme-type.enum.ts b/libs/common/src/platform/enums/theme-type.enum.ts index d1767c4990a..c0f9aa2d52f 100644 --- a/libs/common/src/platform/enums/theme-type.enum.ts +++ b/libs/common/src/platform/enums/theme-type.enum.ts @@ -1,6 +1,8 @@ /** * @deprecated prefer the `ThemeTypes` constants and `Theme` type over unsafe enum types **/ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ThemeType { System = "system", Light = "light", diff --git a/libs/common/src/platform/services/cryptography/initializer-key.ts b/libs/common/src/platform/services/cryptography/initializer-key.ts index 88e36d90515..59287a62c46 100644 --- a/libs/common/src/platform/services/cryptography/initializer-key.ts +++ b/libs/common/src/platform/services/cryptography/initializer-key.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum InitializerKey { Cipher = 0, CipherView = 1, diff --git a/libs/common/src/state-migrations/migrations/28-move-provider-state-to-state-provider.ts b/libs/common/src/state-migrations/migrations/28-move-provider-state-to-state-provider.ts index 96f0a9acd5d..5ae943438d2 100644 --- a/libs/common/src/state-migrations/migrations/28-move-provider-state-to-state-provider.ts +++ b/libs/common/src/state-migrations/migrations/28-move-provider-state-to-state-provider.ts @@ -5,6 +5,8 @@ import { Jsonify } from "type-fest"; import { KeyDefinitionLike, MigrationHelper } from "../migration-helper"; import { Migrator } from "../migrator"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum ProviderUserStatusType { Invited = 0, Accepted = 1, @@ -12,6 +14,8 @@ enum ProviderUserStatusType { Revoked = -1, } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum ProviderUserType { ProviderAdmin = 0, ServiceUser = 1, diff --git a/libs/common/src/state-migrations/migrations/30-move-policy-state-to-state-provider.ts b/libs/common/src/state-migrations/migrations/30-move-policy-state-to-state-provider.ts index 0b4c95cb55c..f188bc0a33e 100644 --- a/libs/common/src/state-migrations/migrations/30-move-policy-state-to-state-provider.ts +++ b/libs/common/src/state-migrations/migrations/30-move-policy-state-to-state-provider.ts @@ -3,6 +3,8 @@ import { KeyDefinitionLike, MigrationHelper } from "../migration-helper"; import { Migrator } from "../migrator"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum PolicyType { TwoFactorAuthentication = 0, // Requires users to have 2fa enabled MasterPassword = 1, // Sets minimum requirements for master password complexity diff --git a/libs/common/src/state-migrations/migrations/40-move-organization-state-to-state-provider.ts b/libs/common/src/state-migrations/migrations/40-move-organization-state-to-state-provider.ts index 862fe33bd4e..b23b712aefe 100644 --- a/libs/common/src/state-migrations/migrations/40-move-organization-state-to-state-provider.ts +++ b/libs/common/src/state-migrations/migrations/40-move-organization-state-to-state-provider.ts @@ -7,6 +7,8 @@ import { Migrator } from "../migrator"; // Local declarations of `OrganizationData` and the types of it's properties. // Duplicated to remain frozen in time when migration occurs. +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum OrganizationUserStatusType { Invited = 0, Accepted = 1, @@ -14,6 +16,8 @@ enum OrganizationUserStatusType { Revoked = -1, } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum OrganizationUserType { Owner = 0, Admin = 1, @@ -40,11 +44,15 @@ type PermissionsApi = { manageScim: boolean; }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum ProviderType { Msp = 0, Reseller = 1, } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum ProductType { Free = 0, Families = 1, diff --git a/libs/common/src/state-migrations/migrations/54-move-encrypted-sends.ts b/libs/common/src/state-migrations/migrations/54-move-encrypted-sends.ts index 7d3a2bebbf9..489fb2258ee 100644 --- a/libs/common/src/state-migrations/migrations/54-move-encrypted-sends.ts +++ b/libs/common/src/state-migrations/migrations/54-move-encrypted-sends.ts @@ -3,6 +3,8 @@ import { KeyDefinitionLike, MigrationHelper } from "../migration-helper"; import { Migrator } from "../migrator"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum SendType { Text = 0, File = 1, diff --git a/libs/common/src/state-migrations/migrations/59-move-kdf-config-to-state-provider.ts b/libs/common/src/state-migrations/migrations/59-move-kdf-config-to-state-provider.ts index 81ee193867b..a01ef42a8ff 100644 --- a/libs/common/src/state-migrations/migrations/59-move-kdf-config-to-state-provider.ts +++ b/libs/common/src/state-migrations/migrations/59-move-kdf-config-to-state-provider.ts @@ -3,6 +3,8 @@ import { KeyDefinitionLike, MigrationHelper } from "../migration-helper"; import { Migrator } from "../migrator"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum KdfType { PBKDF2_SHA256 = 0, Argon2id = 1, diff --git a/libs/common/src/state-migrations/migrations/62-migrate-vault-timeout-settings-svc-to-state-provider.ts b/libs/common/src/state-migrations/migrations/62-migrate-vault-timeout-settings-svc-to-state-provider.ts index 5874a80dc60..3557bfae083 100644 --- a/libs/common/src/state-migrations/migrations/62-migrate-vault-timeout-settings-svc-to-state-provider.ts +++ b/libs/common/src/state-migrations/migrations/62-migrate-vault-timeout-settings-svc-to-state-provider.ts @@ -57,6 +57,8 @@ const vaultTimeoutTypeRollbackRecord: Record = { onIdle: -4, }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ClientType { Web = "web", Browser = "browser", diff --git a/libs/common/src/tools/send/enums/send-type.ts b/libs/common/src/tools/send/enums/send-type.ts index 487930c90c7..5b03c71d22a 100644 --- a/libs/common/src/tools/send/enums/send-type.ts +++ b/libs/common/src/tools/send/enums/send-type.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum SendType { Text = 0, File = 1, diff --git a/libs/common/src/vault/enums/cipher-reprompt-type.ts b/libs/common/src/vault/enums/cipher-reprompt-type.ts index 1d0a523ced0..190a9bad042 100644 --- a/libs/common/src/vault/enums/cipher-reprompt-type.ts +++ b/libs/common/src/vault/enums/cipher-reprompt-type.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum CipherRepromptType { None = 0, Password = 1, diff --git a/libs/common/src/vault/enums/cipher-type.ts b/libs/common/src/vault/enums/cipher-type.ts index 0b7bbf1ee17..30d80cdef7e 100644 --- a/libs/common/src/vault/enums/cipher-type.ts +++ b/libs/common/src/vault/enums/cipher-type.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum CipherType { Login = 1, SecureNote = 2, diff --git a/libs/common/src/vault/enums/field-type.enum.ts b/libs/common/src/vault/enums/field-type.enum.ts index d6deb30e691..df5016890b2 100644 --- a/libs/common/src/vault/enums/field-type.enum.ts +++ b/libs/common/src/vault/enums/field-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum FieldType { Text = 0, Hidden = 1, diff --git a/libs/common/src/vault/enums/linked-id-type.enum.ts b/libs/common/src/vault/enums/linked-id-type.enum.ts index c38ebc1c6e8..b329aecb3f4 100644 --- a/libs/common/src/vault/enums/linked-id-type.enum.ts +++ b/libs/common/src/vault/enums/linked-id-type.enum.ts @@ -1,12 +1,16 @@ export type LinkedIdType = LoginLinkedId | CardLinkedId | IdentityLinkedId; // LoginView +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum LoginLinkedId { Username = 100, Password = 101, } // CardView +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum CardLinkedId { CardholderName = 300, ExpMonth = 301, @@ -17,6 +21,8 @@ export enum CardLinkedId { } // IdentityView +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum IdentityLinkedId { Title = 400, MiddleName = 401, diff --git a/libs/common/src/vault/enums/secure-note-type.enum.ts b/libs/common/src/vault/enums/secure-note-type.enum.ts index 8015236d148..4fbd05e6bd4 100644 --- a/libs/common/src/vault/enums/secure-note-type.enum.ts +++ b/libs/common/src/vault/enums/secure-note-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum SecureNoteType { Generic = 0, } diff --git a/libs/common/src/vault/tasks/enums/security-task-status.enum.ts b/libs/common/src/vault/tasks/enums/security-task-status.enum.ts index 1c6e7decc20..c8c26266e66 100644 --- a/libs/common/src/vault/tasks/enums/security-task-status.enum.ts +++ b/libs/common/src/vault/tasks/enums/security-task-status.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum SecurityTaskStatus { /** * Default status for newly created tasks that have not been completed. diff --git a/libs/common/src/vault/tasks/enums/security-task-type.enum.ts b/libs/common/src/vault/tasks/enums/security-task-type.enum.ts index 264cd88696b..79a2d23c8b3 100644 --- a/libs/common/src/vault/tasks/enums/security-task-type.enum.ts +++ b/libs/common/src/vault/tasks/enums/security-task-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum SecurityTaskType { /** * Task to update a cipher's password that was found to be at-risk by an administrator diff --git a/libs/components/src/color-password/color-password.component.ts b/libs/components/src/color-password/color-password.component.ts index 86ccb41ba3c..2dd78e8525d 100644 --- a/libs/components/src/color-password/color-password.component.ts +++ b/libs/components/src/color-password/color-password.component.ts @@ -2,6 +2,8 @@ import { Component, computed, HostBinding, input } from "@angular/core"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums enum CharacterType { Letter, Emoji, diff --git a/libs/eslint/platform/index.mjs b/libs/eslint/platform/index.mjs index 049d95e2074..c7ea3f1dd89 100644 --- a/libs/eslint/platform/index.mjs +++ b/libs/eslint/platform/index.mjs @@ -1,3 +1,4 @@ import requiredUsing from "./required-using.mjs"; +import noEnums from "./no-enums.mjs"; -export default { rules: { "required-using": requiredUsing } }; +export default { rules: { "required-using": requiredUsing, "no-enums": noEnums } }; diff --git a/libs/eslint/platform/no-enums.mjs b/libs/eslint/platform/no-enums.mjs new file mode 100644 index 00000000000..4f3039f7e34 --- /dev/null +++ b/libs/eslint/platform/no-enums.mjs @@ -0,0 +1,23 @@ +export const errorMessage = "Enums are discouraged, please use a const object instead"; + +export default { + meta: { + type: "suggestion", + docs: { + description: "Enforce using consts instead of enums", + category: "Best Practices", + recommended: false, + }, + schema: [], + }, + create(context) { + return { + TSEnumDeclaration(node) { + context.report({ + node, + message: errorMessage, + }); + }, + }; + }, +}; diff --git a/libs/eslint/platform/no-enums.spec.mjs b/libs/eslint/platform/no-enums.spec.mjs new file mode 100644 index 00000000000..5ded12d6ed3 --- /dev/null +++ b/libs/eslint/platform/no-enums.spec.mjs @@ -0,0 +1,71 @@ +import { RuleTester } from "@typescript-eslint/rule-tester"; + +import rule, { errorMessage } from "./no-enums.mjs"; + +const ruleTester = new RuleTester({ + languageOptions: { + parserOptions: { + project: [__dirname + "/../tsconfig.spec.json"], + projectService: { + allowDefaultProject: ["*.ts*"], + }, + tsconfigRootDir: __dirname + "/..", + }, + }, +}); + +ruleTester.run("no-enums", rule.default, { + valid: [ + { + name: "Using const instead of enum", + code: ` + const Status = { + Active: "active", + Inactive: "inactive", + } as const; + `, + }, + { + name: "Using const with type", + code: ` + const Status = { + Active: "active", + Inactive: "inactive", + } as const; + type Status = typeof Status[keyof typeof Status]; + `, + }, + ], + invalid: [ + { + name: "Using enum declaration", + code: ` + enum Status { + Active = "active", + Inactive = "inactive", + } + `, + errors: [ + { + message: errorMessage, + }, + ], + }, + { + name: "Using enum with numeric values", + code: ` + enum Direction { + Up = 1, + Down = 2, + Left = 3, + Right = 4, + } + `, + errors: [ + { + message: errorMessage, + }, + ], + }, + ], +}); diff --git a/libs/importer/src/importers/fsecure/fsecure-fsk-types.ts b/libs/importer/src/importers/fsecure/fsecure-fsk-types.ts index 71797a0f8cd..1235426d683 100644 --- a/libs/importer/src/importers/fsecure/fsecure-fsk-types.ts +++ b/libs/importer/src/importers/fsecure/fsecure-fsk-types.ts @@ -6,6 +6,8 @@ export interface Data { [key: string]: FskEntry; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum FskEntryTypesEnum { Login = 1, CreditCard = 2, diff --git a/libs/importer/src/importers/lastpass/access/enums/idp-provider.ts b/libs/importer/src/importers/lastpass/access/enums/idp-provider.ts index 32e74c36ee1..01c4572fcf9 100644 --- a/libs/importer/src/importers/lastpass/access/enums/idp-provider.ts +++ b/libs/importer/src/importers/lastpass/access/enums/idp-provider.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum IdpProvider { Azure = 0, OktaAuthServer = 1, diff --git a/libs/importer/src/importers/lastpass/access/enums/lastpass-login-type.ts b/libs/importer/src/importers/lastpass/access/enums/lastpass-login-type.ts index 611dd0b6dab..a3be36c790e 100644 --- a/libs/importer/src/importers/lastpass/access/enums/lastpass-login-type.ts +++ b/libs/importer/src/importers/lastpass/access/enums/lastpass-login-type.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum LastpassLoginType { MasterPassword = 0, // Not sure what Types 1 and 2 are? diff --git a/libs/importer/src/importers/lastpass/access/enums/otp-method.ts b/libs/importer/src/importers/lastpass/access/enums/otp-method.ts index 6b940486ff0..f1237160179 100644 --- a/libs/importer/src/importers/lastpass/access/enums/otp-method.ts +++ b/libs/importer/src/importers/lastpass/access/enums/otp-method.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum OtpMethod { GoogleAuth, MicrosoftAuth, diff --git a/libs/importer/src/importers/lastpass/access/enums/platform.ts b/libs/importer/src/importers/lastpass/access/enums/platform.ts index 283e0c36ce2..6870fc28c24 100644 --- a/libs/importer/src/importers/lastpass/access/enums/platform.ts +++ b/libs/importer/src/importers/lastpass/access/enums/platform.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum Platform { Desktop, Mobile, diff --git a/libs/importer/src/importers/onepassword/types/onepassword-1pux-importer-types.ts b/libs/importer/src/importers/onepassword/types/onepassword-1pux-importer-types.ts index 63a0427b2c7..d7f4dec8f95 100644 --- a/libs/importer/src/importers/onepassword/types/onepassword-1pux-importer-types.ts +++ b/libs/importer/src/importers/onepassword/types/onepassword-1pux-importer-types.ts @@ -25,6 +25,8 @@ export interface VaultAttributes { type: string; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum CategoryEnum { Login = "001", CreditCard = "002", @@ -67,6 +69,8 @@ export interface Details { password?: string | null; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum LoginFieldTypeEnum { TextOrHtml = "T", EmailAddress = "E", diff --git a/libs/importer/src/importers/protonpass/types/protonpass-json-type.ts b/libs/importer/src/importers/protonpass/types/protonpass-json-type.ts index 20fa314a314..af2eb15a740 100644 --- a/libs/importer/src/importers/protonpass/types/protonpass-json-type.ts +++ b/libs/importer/src/importers/protonpass/types/protonpass-json-type.ts @@ -27,6 +27,8 @@ export type ProtonPassItem = { pinned: boolean; }; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum ProtonPassItemState { ACTIVE = 1, TRASHED = 2, diff --git a/libs/key-management/src/biometrics/biometrics-commands.ts b/libs/key-management/src/biometrics/biometrics-commands.ts index 81f0ea747e4..1ef31a31fb4 100644 --- a/libs/key-management/src/biometrics/biometrics-commands.ts +++ b/libs/key-management/src/biometrics/biometrics-commands.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum BiometricsCommands { /** Perform biometric authentication for the system's user. Does not require setup, and does not return cryptographic material, only yes or no. */ AuthenticateWithBiometrics = "authenticateWithBiometrics", diff --git a/libs/key-management/src/biometrics/biometrics-status.ts b/libs/key-management/src/biometrics/biometrics-status.ts index fb46ed1c4be..e436574ff7d 100644 --- a/libs/key-management/src/biometrics/biometrics-status.ts +++ b/libs/key-management/src/biometrics/biometrics-status.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum BiometricsStatus { /** For the biometrics interface, this means that biometric unlock is available and can be used. Querying for the user specifically, this means that biometric can be used for to unlock this user */ Available, diff --git a/libs/key-management/src/enums/kdf-type.enum.ts b/libs/key-management/src/enums/kdf-type.enum.ts index 29fcd9f1f8e..4f468e63a29 100644 --- a/libs/key-management/src/enums/kdf-type.enum.ts +++ b/libs/key-management/src/enums/kdf-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum KdfType { PBKDF2_SHA256 = 0, Argon2id = 1, diff --git a/libs/tools/export/vault-export/vault-export-ui/src/enums/encrypted-export-type.enum.ts b/libs/tools/export/vault-export/vault-export-ui/src/enums/encrypted-export-type.enum.ts index 4767869f7d0..2f416e4a49a 100644 --- a/libs/tools/export/vault-export/vault-export-ui/src/enums/encrypted-export-type.enum.ts +++ b/libs/tools/export/vault-export/vault-export-ui/src/enums/encrypted-export-type.enum.ts @@ -1,3 +1,5 @@ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum EncryptedExportType { AccountEncrypted = 0, FileEncrypted = 1, diff --git a/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.ts b/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.ts index 5da2c6f8bed..0bb753d3f37 100644 --- a/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.ts +++ b/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.ts @@ -35,6 +35,8 @@ export interface SendItemDialogParams { disableForm?: boolean; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum SendItemDialogResult { /** * A Send was saved (created or updated). diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts index c77c44467d8..9ca9aefb4ac 100644 --- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts +++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts @@ -30,6 +30,8 @@ import { SendFileDetailsComponent } from "./send-file-details.component"; import { SendTextDetailsComponent } from "./send-text-details.component"; // Value = hours +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum DatePreset { OneHour = 1, OneDay = 24, diff --git a/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts b/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts index 68660c4bbd9..b34d0d3a312 100644 --- a/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts +++ b/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts @@ -24,6 +24,8 @@ export interface AttachmentsDialogParams { /** * Enum representing the possible results of the attachment dialog. */ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AttachmentDialogResult { Uploaded = "uploaded", Removed = "removed", diff --git a/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts b/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts index c8559799eec..dd3cbc4c5c9 100644 --- a/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts +++ b/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts @@ -34,6 +34,8 @@ import { } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum AddEditFolderDialogResult { Created = "created", Deleted = "deleted", diff --git a/libs/vault/src/components/assign-collections.component.ts b/libs/vault/src/components/assign-collections.component.ts index 45d25a037e9..6a0c45cfbe3 100644 --- a/libs/vault/src/components/assign-collections.component.ts +++ b/libs/vault/src/components/assign-collections.component.ts @@ -80,6 +80,8 @@ export interface CollectionAssignmentParams { isSingleCipherAdmin?: boolean; } +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum CollectionAssignmentResult { Saved = "saved", Canceled = "canceled", diff --git a/libs/vault/src/services/vault-nudges.service.ts b/libs/vault/src/services/vault-nudges.service.ts index be6e978eaa0..d27cd09e954 100644 --- a/libs/vault/src/services/vault-nudges.service.ts +++ b/libs/vault/src/services/vault-nudges.service.ts @@ -23,6 +23,8 @@ export type NudgeStatus = { /** * Enum to list the various nudge types, to be used by components/badges to show/hide the nudge */ +// FIXME: update to use a const object instead of a typescript enum +// eslint-disable-next-line @bitwarden/platform/no-enums export enum VaultNudgeType { /** Nudge to show when user has no items in their vault * Add future nudges here From 4c68f61d47a2fc8deb4300d9beec568c4a11d555 Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Tue, 13 May 2025 10:58:48 -0400 Subject: [PATCH 39/44] feat(CLI-SSO-Login): [Auth/PM-21116] CLI - SSO Login - Add SSO Org Identifier option (#14605) * Add --identifier option for SSO on CLI * Add option for identifier * Moved auto-submit after the setting of client arguments * Adjusted comment * Changed to pass in as SSO option * Renamed to orgSsoIdentifier for clarity * Added more changes to orgSsoIdentifier. --- apps/cli/src/auth/commands/login.command.ts | 7 +++++- apps/cli/src/program.ts | 5 +++- libs/auth/src/angular/sso/sso.component.ts | 16 ++++++------- .../sso-redirect/sso-url.service.spec.ts | 23 +++++++++++++++++++ .../services/sso-redirect/sso-url.service.ts | 6 +++++ 5 files changed, 47 insertions(+), 10 deletions(-) diff --git a/apps/cli/src/auth/commands/login.command.ts b/apps/cli/src/auth/commands/login.command.ts index 3ad71c62e66..cd5c8ef9bcd 100644 --- a/apps/cli/src/auth/commands/login.command.ts +++ b/apps/cli/src/auth/commands/login.command.ts @@ -106,6 +106,8 @@ export class LoginCommand { return Response.badRequest("client_secret is required."); } } else if (options.sso != null && this.canInteract) { + // If the optional Org SSO Identifier isn't provided, the option value is `true`. + const orgSsoIdentifier = options.sso === true ? null : options.sso; const passwordOptions: any = { type: "password", length: 64, @@ -119,7 +121,7 @@ export class LoginCommand { const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, "sha256"); const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash); try { - const ssoParams = await this.openSsoPrompt(codeChallenge, state); + const ssoParams = await this.openSsoPrompt(codeChallenge, state, orgSsoIdentifier); ssoCode = ssoParams.ssoCode; orgIdentifier = ssoParams.orgIdentifier; } catch { @@ -664,6 +666,7 @@ export class LoginCommand { private async openSsoPrompt( codeChallenge: string, state: string, + orgSsoIdentifier: string, ): Promise<{ ssoCode: string; orgIdentifier: string }> { const env = await firstValueFrom(this.environmentService.environment$); @@ -712,6 +715,8 @@ export class LoginCommand { this.ssoRedirectUri, state, codeChallenge, + null, + orgSsoIdentifier, ); this.platformUtilsService.launchUri(webAppSsoUrl); }); diff --git a/apps/cli/src/program.ts b/apps/cli/src/program.ts index dca4effcdc9..d85f1b366e6 100644 --- a/apps/cli/src/program.ts +++ b/apps/cli/src/program.ts @@ -118,7 +118,10 @@ export class Program extends BaseProgram { .description("Log into a user account.") .option("--method ", "Two-step login method.") .option("--code ", "Two-step login code.") - .option("--sso", "Log in with Single-Sign On.") + .option( + "--sso [identifier]", + "Log in with Single-Sign On with optional organization identifier.", + ) .option("--apikey", "Log in with an Api Key.") .option("--passwordenv ", "Environment variable storing your password") .option( diff --git a/libs/auth/src/angular/sso/sso.component.ts b/libs/auth/src/angular/sso/sso.component.ts index a91a8ed20e9..968a05bf850 100644 --- a/libs/auth/src/angular/sso/sso.component.ts +++ b/libs/auth/src/angular/sso/sso.component.ts @@ -155,7 +155,14 @@ export class SsoComponent implements OnInit { return; } - // Detect if we have landed here but only have an SSO identifier in the URL. + // Detect if we are on the first portion of the SSO flow + // and have been sent here from another client with the info in query params. + // If so, we want to initialize the SSO flow with those values. + if (this.hasParametersFromOtherClientRedirect(qParams)) { + this.initializeFromRedirectFromOtherClient(qParams); + } + + // Detect if we have landed here with an SSO identifier in the URL. // This is used by integrations that want to "short-circuit" the login to send users // directly to their IdP to simulate IdP-initiated SSO, so we submit automatically. if (qParams.identifier != null) { @@ -165,13 +172,6 @@ export class SsoComponent implements OnInit { return; } - // Detect if we are on the first portion of the SSO flow - // and have been sent here from another client with the info in query params. - // If so, we want to initialize the SSO flow with those values. - if (this.hasParametersFromOtherClientRedirect(qParams)) { - this.initializeFromRedirectFromOtherClient(qParams); - } - // Try to determine the identifier using claimed domain or local state // persisted from the user's last login attempt. await this.initializeIdentifierFromEmailOrStorage(); diff --git a/libs/auth/src/common/services/sso-redirect/sso-url.service.spec.ts b/libs/auth/src/common/services/sso-redirect/sso-url.service.spec.ts index 074c3a1e0b1..632a2812cfe 100644 --- a/libs/auth/src/common/services/sso-redirect/sso-url.service.spec.ts +++ b/libs/auth/src/common/services/sso-redirect/sso-url.service.spec.ts @@ -92,4 +92,27 @@ describe("SsoUrlService", () => { ); expect(result).toBe(expectedUrl); }); + + it("should build CLI SSO URL with Org SSO Identifier correctly", () => { + const baseUrl = "https://web-vault.bitwarden.com"; + const clientType = ClientType.Cli; + const redirectUri = "https://localhost:1000"; + const state = "abc123"; + const codeChallenge = "xyz789"; + const email = "test@bitwarden.com"; + const orgSsoIdentifier = "test-org"; + + const expectedUrl = `${baseUrl}/#/sso?clientId=cli&redirectUri=${encodeURIComponent(redirectUri)}&state=${state}&codeChallenge=${codeChallenge}&email=${encodeURIComponent(email)}&identifier=${encodeURIComponent(orgSsoIdentifier)}`; + + const result = service.buildSsoUrl( + baseUrl, + clientType, + redirectUri, + state, + codeChallenge, + email, + orgSsoIdentifier, + ); + expect(result).toBe(expectedUrl); + }); }); diff --git a/libs/auth/src/common/services/sso-redirect/sso-url.service.ts b/libs/auth/src/common/services/sso-redirect/sso-url.service.ts index 667a27ad598..b2d6231db7c 100644 --- a/libs/auth/src/common/services/sso-redirect/sso-url.service.ts +++ b/libs/auth/src/common/services/sso-redirect/sso-url.service.ts @@ -11,6 +11,7 @@ export class SsoUrlService { * @param state A state value that will be peristed through the SSO flow * @param codeChallenge A challenge value that will be used to verify the SSO code after authentication * @param email The optional email adddress of the user initiating SSO, which will be used to look up the org SSO identifier + * @param orgSsoIdentifier The optional SSO identifier of the org that is initiating SSO * @returns The URL for redirecting users to the web app SSO component */ buildSsoUrl( @@ -20,6 +21,7 @@ export class SsoUrlService { state: string, codeChallenge: string, email?: string, + orgSsoIdentifier?: string, ): string { let url = webAppUrl + @@ -36,6 +38,10 @@ export class SsoUrlService { url += "&email=" + encodeURIComponent(email); } + if (orgSsoIdentifier) { + url += "&identifier=" + encodeURIComponent(orgSsoIdentifier); + } + return url; } } From 9a7089594efac32006eacd1e013488447f1f4214 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Tue, 13 May 2025 16:59:56 +0200 Subject: [PATCH 40/44] [PM-19878] Add core-js to cli (#14696) * Add core-js to cli * Add core-js to bit-cli --- apps/cli/package.json | 1 + bitwarden_license/bit-cli/src/bw.ts | 2 ++ package-lock.json | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 82faa7d40e6..b01c96b23d1 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -71,6 +71,7 @@ "browser-hrtime": "1.1.8", "chalk": "4.1.2", "commander": "11.1.0", + "core-js": "3.40.0", "form-data": "4.0.1", "https-proxy-agent": "7.0.6", "inquirer": "8.2.6", diff --git a/bitwarden_license/bit-cli/src/bw.ts b/bitwarden_license/bit-cli/src/bw.ts index ffbc186d9e0..2e4e945b9c8 100644 --- a/bitwarden_license/bit-cli/src/bw.ts +++ b/bitwarden_license/bit-cli/src/bw.ts @@ -1,3 +1,5 @@ +import "core-js/proposals/explicit-resource-management"; + import { program } from "commander"; import { registerOssPrograms } from "@bitwarden/cli/register-oss-programs"; diff --git a/package-lock.json b/package-lock.json index 8466a4ba4c6..d1378d63ec3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -207,6 +207,7 @@ "browser-hrtime": "1.1.8", "chalk": "4.1.2", "commander": "11.1.0", + "core-js": "3.40.0", "form-data": "4.0.1", "https-proxy-agent": "7.0.6", "inquirer": "8.2.6", @@ -16960,7 +16961,6 @@ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", "hasInstallScript": true, - "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" From 0750ff38f580e85e7ec6296e3883ac7c38676349 Mon Sep 17 00:00:00 2001 From: SmithThe4th Date: Tue, 13 May 2025 11:21:07 -0400 Subject: [PATCH 41/44] Route to vault page when user doesn't have access (#14633) --- apps/browser/src/_locales/en/messages.json | 3 +++ .../src/vault/guards/at-risk-passwords.guard.ts | 12 ++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 361093b12ef..e1a3234a61f 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -5321,5 +5321,8 @@ "message": "Learn more about SSH agent", "description": "Two part message", "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" + }, + "noPermissionsViewPage": { + "message": "You do not have permissions to view this page. Try logging in with a different account." } } diff --git a/apps/browser/src/vault/guards/at-risk-passwords.guard.ts b/apps/browser/src/vault/guards/at-risk-passwords.guard.ts index 6bcdddfde81..fc302dd6c36 100644 --- a/apps/browser/src/vault/guards/at-risk-passwords.guard.ts +++ b/apps/browser/src/vault/guards/at-risk-passwords.guard.ts @@ -1,6 +1,6 @@ import { inject } from "@angular/core"; -import { CanActivateFn } from "@angular/router"; -import { switchMap, tap } from "rxjs"; +import { CanActivateFn, Router } from "@angular/router"; +import { map, switchMap } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -13,18 +13,22 @@ export const canAccessAtRiskPasswords: CanActivateFn = () => { const taskService = inject(TaskService); const toastService = inject(ToastService); const i18nService = inject(I18nService); + const router = inject(Router); return accountService.activeAccount$.pipe( filterOutNullish(), switchMap((user) => taskService.tasksEnabled$(user.id)), - tap((tasksEnabled) => { + map((tasksEnabled) => { if (!tasksEnabled) { toastService.showToast({ variant: "error", title: "", - message: i18nService.t("accessDenied"), + message: i18nService.t("noPermissionsViewPage"), }); + + return router.createUrlTree(["/tabs/vault"]); } + return true; }), ); }; From 1f72dd7710a160058cd39316ac00d97cdbfe5aad Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Tue, 13 May 2025 11:30:36 -0400 Subject: [PATCH 42/44] PM-21286 add aria label to nudge settings badge (#14750) --- apps/browser/src/_locales/en/messages.json | 3 +++ .../src/tools/popup/settings/settings-v2.component.html | 3 +++ 2 files changed, 6 insertions(+) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index e1a3234a61f..dcdfe7df4d6 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -5258,6 +5258,9 @@ "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." }, + "nudgeBadgeAria": { + "message": "1 notification" + }, "emptyVaultNudgeTitle": { "message": "Import existing passwords" }, diff --git a/apps/browser/src/tools/popup/settings/settings-v2.component.html b/apps/browser/src/tools/popup/settings/settings-v2.component.html index 22e2d9a28d0..8d31ccf8371 100644 --- a/apps/browser/src/tools/popup/settings/settings-v2.component.html +++ b/apps/browser/src/tools/popup/settings/settings-v2.component.html @@ -23,6 +23,7 @@ *ngIf="!isBrowserAutofillSettingOverridden && (showAutofillBadge$ | async)" bitBadge variant="notification" + [attr.aria-label]="'nudgeBadgeAria' | i18n" >1
@@ -53,6 +54,7 @@ *ngIf="!(showVaultBadge$ | async)?.hasBadgeDismissed" bitBadge variant="notification" + [attr.aria-label]="'nudgeBadgeAria' | i18n" >1 @@ -83,6 +85,7 @@ *ngIf="(downloadBitwardenNudgeStatus$ | async)?.hasBadgeDismissed === false" bitBadge variant="notification" + [attr.aria-label]="'nudgeBadgeAria' | i18n" >1 From 5fb46df3415aefced0b52f2db86c873962255448 Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Tue, 13 May 2025 16:49:06 +0100 Subject: [PATCH 43/44] [PM 21106]Remove button not responsive for admin Console Remove Sponorship (#14743) * Resolve the remove button inactive * Resolve the lint error --- .../free-bitwarden-families.component.ts | 2 +- .../settings/sponsoring-org-row.component.ts | 2 +- .../src/services/jslib-services.module.ts | 2 +- libs/common/src/abstractions/api.service.ts | 1 - ...ion-sponsorship-api.service.abstraction.ts | 5 ++++ .../organization-sponsorship-api.service.ts | 23 ++++++++++++++++++- libs/common/src/services/api.service.ts | 12 ---------- 7 files changed, 30 insertions(+), 17 deletions(-) diff --git a/apps/web/src/app/billing/members/free-bitwarden-families.component.ts b/apps/web/src/app/billing/members/free-bitwarden-families.component.ts index eb6bfdf368b..b482007e30b 100644 --- a/apps/web/src/app/billing/members/free-bitwarden-families.component.ts +++ b/apps/web/src/app/billing/members/free-bitwarden-families.component.ts @@ -179,7 +179,7 @@ export class FreeBitwardenFamiliesComponent implements OnInit { return; } - await this.apiService.deleteRevokeSponsorship(this.organizationId); + await this.organizationSponsorshipApiService.deleteRevokeSponsorship(this.organizationId, true); this.toastService.showToast({ variant: "success", diff --git a/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts b/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts index 33e6334c577..39a7531082a 100644 --- a/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts +++ b/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts @@ -109,7 +109,7 @@ export class SponsoringOrgRowComponent implements OnInit { return; } - await this.apiService.deleteRevokeSponsorship(this.sponsoringOrg.id); + await this.organizationSponsorshipApiService.deleteRevokeSponsorship(this.sponsoringOrg.id); this.toastService.showToast({ variant: "success", title: null, diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 470115ae3f0..3ffca776034 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -1079,7 +1079,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: OrganizationSponsorshipApiServiceAbstraction, useClass: OrganizationSponsorshipApiService, - deps: [ApiServiceAbstraction], + deps: [ApiServiceAbstraction, PlatformUtilsServiceAbstraction], }), safeProvider({ provide: OrganizationBillingApiServiceAbstraction, diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index 1e13a3064f4..e4453359015 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -475,7 +475,6 @@ export abstract class ApiService { getSponsorshipSyncStatus: ( sponsoredOrgId: string, ) => Promise; - deleteRevokeSponsorship: (sponsoringOrganizationId: string) => Promise; deleteRemoveSponsorship: (sponsoringOrgId: string) => Promise; postPreValidateSponsorshipToken: ( sponsorshipToken: string, diff --git a/libs/common/src/billing/abstractions/organizations/organization-sponsorship-api.service.abstraction.ts b/libs/common/src/billing/abstractions/organizations/organization-sponsorship-api.service.abstraction.ts index 7bd6aac17bd..5e69f57ca19 100644 --- a/libs/common/src/billing/abstractions/organizations/organization-sponsorship-api.service.abstraction.ts +++ b/libs/common/src/billing/abstractions/organizations/organization-sponsorship-api.service.abstraction.ts @@ -10,4 +10,9 @@ export abstract class OrganizationSponsorshipApiServiceAbstraction { sponsoringOrgId: string, friendlyName?: string, ): Promise; + + abstract deleteRevokeSponsorship: ( + sponsoringOrganizationId: string, + isAdminInitiated?: boolean, + ) => Promise; } diff --git a/libs/common/src/billing/services/organization/organization-sponsorship-api.service.ts b/libs/common/src/billing/services/organization/organization-sponsorship-api.service.ts index 22ab3ec86ee..99440b10dec 100644 --- a/libs/common/src/billing/services/organization/organization-sponsorship-api.service.ts +++ b/libs/common/src/billing/services/organization/organization-sponsorship-api.service.ts @@ -1,12 +1,16 @@ import { ApiService } from "../../../abstractions/api.service"; import { ListResponse } from "../../../models/response/list.response"; +import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service"; import { OrganizationSponsorshipApiServiceAbstraction } from "../../abstractions/organizations/organization-sponsorship-api.service.abstraction"; import { OrganizationSponsorshipInvitesResponse } from "../../models/response/organization-sponsorship-invites.response"; export class OrganizationSponsorshipApiService implements OrganizationSponsorshipApiServiceAbstraction { - constructor(private apiService: ApiService) {} + constructor( + private apiService: ApiService, + private platformUtilsService: PlatformUtilsService, + ) {} async getOrganizationSponsorship( sponsoredOrgId: string, ): Promise> { @@ -33,4 +37,21 @@ export class OrganizationSponsorshipApiService return await this.apiService.send("POST", url, null, true, false); } + + async deleteRevokeSponsorship( + sponsoringOrganizationId: string, + isAdminInitiated: boolean = false, + ): Promise { + const basePath = "/organization/sponsorship/"; + const hostPath = this.platformUtilsService.isSelfHost() ? "self-hosted/" : ""; + const queryParam = `?isAdminInitiated=${isAdminInitiated}`; + + return await this.apiService.send( + "DELETE", + basePath + hostPath + sponsoringOrganizationId + queryParam, + null, + true, + false, + ); + } } diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index 5c4bcdedb26..639daa7c658 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -1621,18 +1621,6 @@ export class ApiService implements ApiServiceAbstraction { return new OrganizationSponsorshipSyncStatusResponse(response); } - async deleteRevokeSponsorship(sponsoringOrganizationId: string): Promise { - return await this.send( - "DELETE", - "/organization/sponsorship/" + - (this.platformUtilsService.isSelfHost() ? "self-hosted/" : "") + - sponsoringOrganizationId, - null, - true, - false, - ); - } - async deleteRemoveSponsorship(sponsoringOrgId: string): Promise { return await this.send( "DELETE", From b3df8a6c13781a41ce5b82b83b35c90c5e32e0f2 Mon Sep 17 00:00:00 2001 From: Vicki League Date: Tue, 13 May 2025 14:16:18 -0400 Subject: [PATCH 44/44] [PM-17091][PM-17043] Support system zoom in browser extension (#14435) --- .../popup/layout/popup-size.service.ts | 37 +++++++++++--- apps/browser/src/popup/app.component.ts | 3 ++ apps/browser/src/popup/scss/base.scss | 49 ++++++++++++------- 3 files changed, 63 insertions(+), 26 deletions(-) diff --git a/apps/browser/src/platform/popup/layout/popup-size.service.ts b/apps/browser/src/platform/popup/layout/popup-size.service.ts index 3ae9a633cab..69d3102d24e 100644 --- a/apps/browser/src/platform/popup/layout/popup-size.service.ts +++ b/apps/browser/src/platform/popup/layout/popup-size.service.ts @@ -50,17 +50,40 @@ export class PopupSizeService { PopupSizeService.setStyle(width); localStorage.setItem(PopupSizeService.LocalStorageKey, width); }); + } + async setHeight() { const isInChromeTab = await BrowserPopupUtils.isInTab(); + /** + * To support both browser default zoom and system default zoom, we need to take into account + * the full screen height. When system default zoom is >100%, window.innerHeight still outputs + * a height equivalent to what it would be at 100%, which can cause the extension window to + * render as too tall. So if the screen height is smaller than the max possible extension height, + * we should use that to set our extension height. Otherwise, we want to use the window.innerHeight + * to support browser zoom. + * + * This is basically a workaround for what we consider a bug with browsers reporting the wrong + * available innerHeight when system zoom is turned on. If that gets fixed, we can remove the code + * checking the screen height. + */ + const MAX_EXT_HEIGHT = 600; + const extensionInnerHeight = window.innerHeight; + // Use a 100px offset when calculating screen height to account for browser container elements + const screenAvailHeight = window.screen.availHeight - 100; + const availHeight = + screenAvailHeight < MAX_EXT_HEIGHT ? screenAvailHeight : extensionInnerHeight; + if (!BrowserPopupUtils.inPopup(window) || isInChromeTab) { - window.document.body.classList.add("body-full"); - } else if (window.innerHeight < 400) { - window.document.body.classList.add("body-xxs"); - } else if (window.innerHeight < 500) { - window.document.body.classList.add("body-xs"); - } else if (window.innerHeight < 600) { - window.document.body.classList.add("body-sm"); + window.document.documentElement.classList.add("body-full"); + } else if (availHeight < 300) { + window.document.documentElement.classList.add("body-3xs"); + } else if (availHeight < 400) { + window.document.documentElement.classList.add("body-xxs"); + } else if (availHeight < 500) { + window.document.documentElement.classList.add("body-xs"); + } else if (availHeight < 600) { + window.document.documentElement.classList.add("body-sm"); } } diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index 49579f889b3..a480e1d6ba3 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -26,6 +26,7 @@ import { import { BiometricsService, BiometricStateService } from "@bitwarden/key-management"; import { PopupCompactModeService } from "../platform/popup/layout/popup-compact-mode.service"; +import { PopupSizeService } from "../platform/popup/layout/popup-size.service"; import { initPopupClosedListener } from "../platform/services/popup-view-cache-background.service"; import { VaultBrowserStateService } from "../vault/services/vault-browser-state.service"; @@ -71,6 +72,7 @@ export class AppComponent implements OnInit, OnDestroy { private biometricStateService: BiometricStateService, private biometricsService: BiometricsService, private deviceTrustToastService: DeviceTrustToastService, + private popupSizeService: PopupSizeService, ) { this.deviceTrustToastService.setupListeners$.pipe(takeUntilDestroyed()).subscribe(); } @@ -79,6 +81,7 @@ export class AppComponent implements OnInit, OnDestroy { initPopupClosedListener(); this.compactModeService.init(); + await this.popupSizeService.setHeight(); // Component states must not persist between closing and reopening the popup, otherwise they become dead objects // Clear them aggressively to make sure this doesn't occur diff --git a/apps/browser/src/popup/scss/base.scss b/apps/browser/src/popup/scss/base.scss index 59893b5050d..80ada61f868 100644 --- a/apps/browser/src/popup/scss/base.scss +++ b/apps/browser/src/popup/scss/base.scss @@ -8,6 +8,34 @@ html { overflow: hidden; + min-height: 600px; + height: 100%; + + &.body-sm { + min-height: 500px; + } + + &.body-xs { + min-height: 400px; + } + + &.body-xxs { + min-height: 300px; + } + + &.body-3xs { + min-height: 240px; + } + + &.body-full { + min-height: unset; + width: 100%; + height: 100%; + + & body { + width: 100%; + } + } } html, @@ -20,9 +48,9 @@ body { body { width: 380px; - height: 600px; + height: 100%; position: relative; - min-height: 100vh; + min-height: inherit; overflow: hidden; color: $text-color; background-color: $background-color; @@ -31,23 +59,6 @@ body { color: themed("textColor"); background-color: themed("backgroundColor"); } - - &.body-sm { - height: 500px; - } - - &.body-xs { - height: 400px; - } - - &.body-xxs { - height: 300px; - } - - &.body-full { - width: 100%; - height: 100%; - } } h1,

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Ð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 Z%W3>CI$@P~5SgeBd{mD4Gdn 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%0DZzBc#{kuHQ3xWI`Ear!Re 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 E0Ln!2jQ{`u literal 0 HcmV?d00001 diff --git a/libs/components/src/avatar/avatar.component.ts b/libs/components/src/avatar/avatar.component.ts index 0e3dbd6f1b9..554f55636fc 100644 --- a/libs/components/src/avatar/avatar.component.ts +++ b/libs/components/src/avatar/avatar.component.ts @@ -123,7 +123,7 @@ export class AvatarComponent implements OnChanges { textTag.setAttribute("fill", Utils.pickTextColorBasedOnBgColor(color, 135, true)); textTag.setAttribute( "font-family", - '"DM Sans","Helvetica Neue",Helvetica,Arial,' + + 'Roboto,"Helvetica Neue",Helvetica,Arial,' + 'sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"', ); // Warning do not use innerHTML here, characters are user provided diff --git a/libs/components/src/variables.scss b/libs/components/src/variables.scss index bc9cded4981..e3651f9c37d 100644 --- a/libs/components/src/variables.scss +++ b/libs/components/src/variables.scss @@ -21,7 +21,7 @@ $body-bg: $white; $body-color: #333333; $font-family-sans-serif: - "DM Sans", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; $h1-font-size: 1.7rem; From 87b875c48b2a9c5d463397a4bdefe42412399a42 Mon Sep 17 00:00:00 2001 From: Patrick-Pimentel-Bitwarden Date: Mon, 12 May 2025 09:29:11 -0400 Subject: [PATCH 15/44] docs(update-auth-approving-clients): [PM-17111] Add Browser to List of Approving Clients - Updated comments. (#14707) --- .../services/platform-utils/browser-platform-utils.service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts index c9200ecc1a4..22708d8e425 100644 --- a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts +++ b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts @@ -26,6 +26,10 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic return this.deviceCache; } + // ORDERING MATTERS HERE + // Ordered from most specific to least specific. We try to discern the greatest detail + // for the type of extension the user is on by checking specific cases first and as we go down + // the list we hope to catch all by the most generic clients they could be on. if (BrowserPlatformUtilsService.isFirefox()) { this.deviceCache = DeviceType.FirefoxExtension; } else if (BrowserPlatformUtilsService.isOpera(globalContext)) { From aca8ab8e40f18108ddbf3a1808c545bc1571b1f7 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 12 May 2025 15:31:03 +0200 Subject: [PATCH 16/44] [PM-21001] Move autofill code to new encrypt service interface (#14548) * Move autofill code to new encrypt service interface * Fix test runner --- .../src/native-message.service.ts | 8 ++------ .../src/services/duckduckgo-message-handler.service.ts | 5 ++--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/apps/desktop/native-messaging-test-runner/src/native-message.service.ts b/apps/desktop/native-messaging-test-runner/src/native-message.service.ts index c01d581afe8..c2356f93e28 100644 --- a/apps/desktop/native-messaging-test-runner/src/native-message.service.ts +++ b/apps/desktop/native-messaging-test-runner/src/native-message.service.ts @@ -220,7 +220,7 @@ export default class NativeMessageService { const sharedKey = await this.getSharedKeyForKey(key); - return this.encryptService.encrypt(commandDataString, sharedKey); + return this.encryptService.encryptString(commandDataString, sharedKey); } private async decryptResponsePayload( @@ -228,11 +228,7 @@ export default class NativeMessageService { key: string, ): Promise { const sharedKey = await this.getSharedKeyForKey(key); - const decrypted = await this.encryptService.decryptToUtf8( - payload, - sharedKey, - "native-messaging-session", - ); + const decrypted = await this.encryptService.decryptString(payload, sharedKey); return JSON.parse(decrypted); } diff --git a/apps/desktop/src/services/duckduckgo-message-handler.service.ts b/apps/desktop/src/services/duckduckgo-message-handler.service.ts index e82444be993..6fb91231be1 100644 --- a/apps/desktop/src/services/duckduckgo-message-handler.service.ts +++ b/apps/desktop/src/services/duckduckgo-message-handler.service.ts @@ -168,7 +168,7 @@ export class DuckDuckGoMessageHandlerService { payload: DecryptedCommandData, key: SymmetricCryptoKey, ): Promise { - return await this.encryptService.encrypt(JSON.stringify(payload), key); + return await this.encryptService.encryptString(JSON.stringify(payload), key); } private async decryptPayload(message: EncryptedMessage): Promise { @@ -188,10 +188,9 @@ export class DuckDuckGoMessageHandlerService { } try { - let decryptedResult = await this.encryptService.decryptToUtf8( + let decryptedResult = await this.encryptService.decryptString( message.encryptedCommand as EncString, this.duckduckgoSharedSecret, - "ddg-shared-key", ); decryptedResult = this.trimNullCharsFromMessage(decryptedResult); From 57ed1e7285f8a263429140a19ae0e0ff32258dc6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 10:09:40 -0400 Subject: [PATCH 17/44] [deps] Platform: Update @types/node to v22.15.3 (#14723) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .../native-messaging-test-runner/package-lock.json | 8 ++++---- apps/desktop/native-messaging-test-runner/package.json | 2 +- package-lock.json | 8 ++++---- package.json | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/desktop/native-messaging-test-runner/package-lock.json b/apps/desktop/native-messaging-test-runner/package-lock.json index d506e109e94..8b39fd9805e 100644 --- a/apps/desktop/native-messaging-test-runner/package-lock.json +++ b/apps/desktop/native-messaging-test-runner/package-lock.json @@ -17,7 +17,7 @@ "yargs": "17.7.2" }, "devDependencies": { - "@types/node": "22.14.1", + "@types/node": "22.15.3", "typescript": "5.4.2" } }, @@ -101,9 +101,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", - "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "version": "22.15.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz", + "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" diff --git a/apps/desktop/native-messaging-test-runner/package.json b/apps/desktop/native-messaging-test-runner/package.json index f67ab259d3b..ea6b1b3e7a8 100644 --- a/apps/desktop/native-messaging-test-runner/package.json +++ b/apps/desktop/native-messaging-test-runner/package.json @@ -22,7 +22,7 @@ "yargs": "17.7.2" }, "devDependencies": { - "@types/node": "22.14.1", + "@types/node": "22.15.3", "typescript": "5.4.2" }, "_moduleAliases": { diff --git a/package-lock.json b/package-lock.json index 1e7e0741cd8..a15275a695e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -110,7 +110,7 @@ "@types/koa-json": "2.0.23", "@types/lowdb": "1.0.15", "@types/lunr": "2.3.7", - "@types/node": "22.14.1", + "@types/node": "22.15.3", "@types/node-fetch": "2.6.4", "@types/node-forge": "1.3.11", "@types/papaparse": "5.3.15", @@ -11656,9 +11656,9 @@ } }, "node_modules/@types/node": { - "version": "22.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", - "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "version": "22.15.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz", + "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 81f1c92d5a5..904ae121cc9 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "@types/koa-json": "2.0.23", "@types/lowdb": "1.0.15", "@types/lunr": "2.3.7", - "@types/node": "22.14.1", + "@types/node": "22.15.3", "@types/node-fetch": "2.6.4", "@types/node-forge": "1.3.11", "@types/papaparse": "5.3.15", From 89b9ba21cabbd4ddaf71bba9f267cd646bd4fc9b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 07:15:06 -0700 Subject: [PATCH 18/44] [deps] Platform: Update Rust crate arboard to v3.5.0 (#14484) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- apps/desktop/desktop_native/Cargo.lock | 153 ++++++++----------------- apps/desktop/desktop_native/Cargo.toml | 2 +- 2 files changed, 46 insertions(+), 109 deletions(-) diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index cfd29303510..1b442fbbb8a 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -120,9 +120,9 @@ checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "arboard" -version = "3.4.1" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" +checksum = "c1df21f715862ede32a0c525ce2ca4d52626bb0007f8c18b87a384503ac33e70" dependencies = [ "clipboard-win", "log", @@ -130,6 +130,7 @@ dependencies = [ "objc2-app-kit", "objc2-foundation", "parking_lot", + "percent-encoding", "wl-clipboard-rs", "x11rb", ] @@ -465,15 +466,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" -dependencies = [ - "objc2", -] - [[package]] name = "blocking" version = "1.6.1" @@ -565,12 +557,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "cfg_aliases" version = "0.2.1" @@ -867,17 +853,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "derive-new" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "desktop_core" version = "0.0.0" @@ -1007,6 +982,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1409,7 +1394,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bdbbd5bc8c5749697ccaa352fa45aff8730cf21c68029c0eef1ffed7c3d6ba2" dependencies = [ "cfg-if", - "nix 0.29.0", + "nix", "widestring", "windows 0.57.0", ] @@ -1839,18 +1824,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases 0.1.1", - "libc", -] - [[package]] name = "nix" version = "0.29.0" @@ -1859,7 +1832,7 @@ checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases", "libc", "memoffset", ] @@ -1990,47 +1963,24 @@ dependencies = [ "libc", ] -[[package]] -name = "objc-sys" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" - [[package]] name = "objc2" -version = "0.5.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" dependencies = [ - "objc-sys", "objc2-encode", ] [[package]] name = "objc2-app-kit" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ "bitflags", - "block2", - "libc", - "objc2", - "objc2-core-data", - "objc2-core-image", - "objc2-foundation", - "objc2-quartz-core", -] - -[[package]] -name = "objc2-core-data" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" -dependencies = [ - "bitflags", - "block2", "objc2", + "objc2-core-graphics", "objc2-foundation", ] @@ -2041,18 +1991,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ "bitflags", + "dispatch2", + "objc2", ] [[package]] -name = "objc2-core-image" -version = "0.2.2" +name = "objc2-core-graphics" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ - "block2", + "bitflags", + "dispatch2", "objc2", - "objc2-foundation", - "objc2-metal", + "objc2-core-foundation", + "objc2-io-surface", ] [[package]] @@ -2063,14 +2016,13 @@ checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] name = "objc2-foundation" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ "bitflags", - "block2", - "libc", "objc2", + "objc2-core-foundation", ] [[package]] @@ -2084,28 +2036,14 @@ dependencies = [ ] [[package]] -name = "objc2-metal" -version = "0.2.2" +name = "objc2-io-surface" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" dependencies = [ "bitflags", - "block2", "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-quartz-core" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" -dependencies = [ - "bitflags", - "block2", - "objc2", - "objc2-foundation", - "objc2-metal", + "objc2-core-foundation", ] [[package]] @@ -3487,9 +3425,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.31.2" +version = "0.32.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" dependencies = [ "bitflags", "wayland-backend", @@ -3499,9 +3437,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.2.0" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" dependencies = [ "bitflags", "wayland-backend", @@ -3982,15 +3920,14 @@ dependencies = [ [[package]] name = "wl-clipboard-rs" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b41773911497b18ca8553c3daaf8ec9fe9819caf93d451d3055f69de028adb" +checksum = "2a083daad7e8a4b8805ad73947ccadabe62afe37ce0e9787a56ff373d34762c7" dependencies = [ - "derive-new", "libc", "log", - "nix 0.28.0", "os_pipe", + "rustix", "tempfile", "thiserror 1.0.69", "tree_magic_mini", @@ -4085,7 +4022,7 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix 0.29.0", + "nix", "ordered-stream", "rand 0.8.5", "serde", @@ -4115,7 +4052,7 @@ dependencies = [ "futures-core", "futures-lite", "hex", - "nix 0.29.0", + "nix", "ordered-stream", "serde", "serde_repr", diff --git a/apps/desktop/desktop_native/Cargo.toml b/apps/desktop/desktop_native/Cargo.toml index f613744f42a..fafaf02eca3 100644 --- a/apps/desktop/desktop_native/Cargo.toml +++ b/apps/desktop/desktop_native/Cargo.toml @@ -11,7 +11,7 @@ publish = false [workspace.dependencies] aes = "=0.8.4" anyhow = "=1.0.94" -arboard = { version = "=3.4.1", default-features = false } +arboard = { version = "=3.5.0", default-features = false } argon2 = "=0.5.3" base64 = "=0.22.1" bindgen = "0.71.1" From 07725853a287c679964a608f3b23695c54030575 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 12 May 2025 17:00:53 +0200 Subject: [PATCH 19/44] Add tests for masterpasswordservice (#14728) * Add tests for masterpasswordservice * Fix tests --- .../services/master-password.service.spec.ts | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/libs/common/src/key-management/master-password/services/master-password.service.spec.ts b/libs/common/src/key-management/master-password/services/master-password.service.spec.ts index 93439ac8caa..b55e770f865 100644 --- a/libs/common/src/key-management/master-password/services/master-password.service.spec.ts +++ b/libs/common/src/key-management/master-password/services/master-password.service.spec.ts @@ -2,12 +2,16 @@ import { mock, MockProxy } from "jest-mock-extended"; import { of } from "rxjs"; import * as rxjs from "rxjs"; +import { makeSymmetricCryptoKey } from "../../../../spec"; import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-password-reason"; import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service"; import { LogService } from "../../../platform/abstractions/log.service"; import { StateService } from "../../../platform/abstractions/state.service"; +import { EncString } from "../../../platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { StateProvider } from "../../../platform/state"; import { UserId } from "../../../types/guid"; +import { MasterKey } from "../../../types/key"; import { EncryptService } from "../../crypto/abstractions/encrypt.service"; import { MasterPasswordService } from "./master-password.service"; @@ -27,6 +31,14 @@ describe("MasterPasswordService", () => { update: jest.fn().mockResolvedValue(null), }; + const testUserKey: SymmetricCryptoKey = makeSymmetricCryptoKey(64, 1); + const testMasterKey: MasterKey = makeSymmetricCryptoKey(32, 2); + const testStretchedMasterKey: SymmetricCryptoKey = makeSymmetricCryptoKey(64, 3); + const testMasterKeyEncryptedKey = + "0.gbauOANURUHqvhLTDnva1A==|nSW+fPumiuTaDB/s12+JO88uemV6rhwRSR+YR1ZzGr5j6Ei3/h+XEli2Unpz652NlZ9NTuRpHxeOqkYYJtp7J+lPMoclgteXuAzUu9kqlRc="; + const testStretchedMasterKeyEncryptedKey = + "2.gbauOANURUHqvhLTDnva1A==|nSW+fPumiuTaDB/s12+JO88uemV6rhwRSR+YR1ZzGr5j6Ei3/h+XEli2Unpz652NlZ9NTuRpHxeOqkYYJtp7J+lPMoclgteXuAzUu9kqlRc=|DeUFkhIwgkGdZA08bDnDqMMNmZk21D+H5g8IostPKAY="; + beforeEach(() => { stateProvider = mock(); stateService = mock(); @@ -45,6 +57,9 @@ describe("MasterPasswordService", () => { encryptService, logService, ); + + encryptService.unwrapSymmetricKey.mockResolvedValue(makeSymmetricCryptoKey(64, 1)); + keyGenerationService.stretchKey.mockResolvedValue(makeSymmetricCryptoKey(64, 3)); }); describe("setForceSetPasswordReason", () => { @@ -101,4 +116,41 @@ describe("MasterPasswordService", () => { expect(mockUserState.update).toHaveBeenCalled(); }); }); + describe("decryptUserKeyWithMasterKey", () => { + it("decrypts a userkey wrapped in AES256-CBC", async () => { + encryptService.unwrapSymmetricKey.mockResolvedValue(testUserKey); + await sut.decryptUserKeyWithMasterKey( + testMasterKey, + userId, + new EncString(testMasterKeyEncryptedKey), + ); + expect(encryptService.unwrapSymmetricKey).toHaveBeenCalledWith( + new EncString(testMasterKeyEncryptedKey), + testMasterKey, + ); + }); + it("decrypts a userkey wrapped in AES256-CBC-HMAC", async () => { + encryptService.unwrapSymmetricKey.mockResolvedValue(testUserKey); + keyGenerationService.stretchKey.mockResolvedValue(testStretchedMasterKey); + await sut.decryptUserKeyWithMasterKey( + testMasterKey, + userId, + new EncString(testStretchedMasterKeyEncryptedKey), + ); + expect(encryptService.unwrapSymmetricKey).toHaveBeenCalledWith( + new EncString(testStretchedMasterKeyEncryptedKey), + testStretchedMasterKey, + ); + expect(keyGenerationService.stretchKey).toHaveBeenCalledWith(testMasterKey); + }); + it("returns null if failed to decrypt", async () => { + encryptService.unwrapSymmetricKey.mockResolvedValue(null); + const result = await sut.decryptUserKeyWithMasterKey( + testMasterKey, + userId, + new EncString(testStretchedMasterKeyEncryptedKey), + ); + expect(result).toBeNull(); + }); + }); }); From 2487e9b98deb32b10a43bd93cc839d4556223ad4 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Mon, 12 May 2025 11:13:49 -0400 Subject: [PATCH 20/44] [PM-20637] Trigger password reprompt when updating a reprompt cipher via notification (#14575) * reprompt user on cipher update when enabled Co-authored-by: Daniel Riera * update tests * add test --------- Co-authored-by: Daniel Riera --- .../notification.background.spec.ts | 57 ++++++++++++++++++- .../background/notification.background.ts | 42 ++++++++++++-- .../overlay-notifications-content.service.ts | 1 + .../overlay-notifications-content.service.ts | 6 +- .../services/abstractions/autofill.service.ts | 6 +- .../src/autofill/services/autofill.service.ts | 9 ++- .../vault-v2/view-v2/view-v2.component.ts | 27 +++++++-- libs/common/src/autofill/constants/index.ts | 3 +- 8 files changed, 134 insertions(+), 17 deletions(-) diff --git a/apps/browser/src/autofill/background/notification.background.spec.ts b/apps/browser/src/autofill/background/notification.background.spec.ts index 63ae1193737..009efd7ff36 100644 --- a/apps/browser/src/autofill/background/notification.background.spec.ts +++ b/apps/browser/src/autofill/background/notification.background.spec.ts @@ -17,6 +17,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service"; import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; import { UserId } from "@bitwarden/common/types/guid"; +import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { CipherService } from "@bitwarden/common/vault/services/cipher.service"; @@ -828,6 +829,7 @@ describe("NotificationBackground", () => { id: "testId", name: "testItemName", login: { username: "testUser" }, + reprompt: CipherRepromptType.None, }); getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView); @@ -842,6 +844,7 @@ describe("NotificationBackground", () => { message.edit, sender.tab, "testId", + false, ); expect(updateWithServerSpy).toHaveBeenCalled(); expect(tabSendMessageDataSpy).toHaveBeenCalledWith( @@ -855,6 +858,55 @@ describe("NotificationBackground", () => { ); }); + it("prompts the user for master password entry if the notification message type is for ChangePassword and the cipher reprompt is enabled", async () => { + const tab = createChromeTabMock({ id: 1, url: "https://example.com" }); + const sender = mock({ tab }); + const message: NotificationBackgroundExtensionMessage = { + command: "bgSaveCipher", + edit: false, + folder: "folder-id", + }; + const queueMessage = mock({ + type: NotificationQueueMessageType.ChangePassword, + tab, + domain: "example.com", + newPassword: "newPassword", + }); + notificationBackground["notificationQueue"] = [queueMessage]; + const cipherView = mock({ + id: "testId", + name: "testItemName", + login: { username: "testUser" }, + reprompt: CipherRepromptType.Password, + }); + getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView); + + sendMockExtensionMessage(message, sender); + await flushPromises(); + + expect(editItemSpy).not.toHaveBeenCalled(); + expect(autofillService.isPasswordRepromptRequired).toHaveBeenCalled(); + expect(createWithServerSpy).not.toHaveBeenCalled(); + expect(updatePasswordSpy).toHaveBeenCalledWith( + cipherView, + queueMessage.newPassword, + message.edit, + sender.tab, + "testId", + false, + ); + expect(updateWithServerSpy).not.toHaveBeenCalled(); + expect(tabSendMessageDataSpy).not.toHaveBeenCalledWith( + sender.tab, + "saveCipherAttemptCompleted", + { + itemName: "testItemName", + cipherId: cipherView.id, + task: undefined, + }, + ); + }); + it("completes password update notification with a security task notice if any are present for the cipher, and dismisses tasks for the updated cipher", async () => { const mockCipherId = "testId"; const mockOrgId = "testOrgId"; @@ -905,6 +957,7 @@ describe("NotificationBackground", () => { id: mockCipherId, organizationId: mockOrgId, name: "Test Item", + reprompt: CipherRepromptType.None, }); getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView); @@ -919,6 +972,7 @@ describe("NotificationBackground", () => { message.edit, sender.tab, mockCipherId, + false, ); expect(updateWithServerSpy).toHaveBeenCalled(); expect(tabSendMessageDataSpy).toHaveBeenCalledWith( @@ -1000,6 +1054,7 @@ describe("NotificationBackground", () => { message.edit, sender.tab, "testId", + false, ); expect(editItemSpy).toHaveBeenCalled(); expect(updateWithServerSpy).not.toHaveBeenCalled(); @@ -1170,7 +1225,7 @@ describe("NotificationBackground", () => { newPassword: "newPassword", }); notificationBackground["notificationQueue"] = [queueMessage]; - const cipherView = mock(); + const cipherView = mock({ reprompt: CipherRepromptType.None }); getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView); const errorMessage = "fetch error"; updateWithServerSpy.mockImplementation(() => { diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index 5dd15274677..52920ec67a8 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -14,6 +14,7 @@ import { ExtensionCommand, ExtensionCommandType, NOTIFICATION_BAR_LIFESPAN_MS, + UPDATE_PASSWORD, } from "@bitwarden/common/autofill/constants"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service"; @@ -104,6 +105,8 @@ export default class NotificationBackground { this.removeTabFromNotificationQueue(sender.tab), bgReopenUnlockPopout: ({ sender }) => this.openUnlockPopout(sender.tab), bgSaveCipher: ({ message, sender }) => this.handleSaveCipherMessage(message, sender), + bgHandleReprompt: ({ message, sender }: any) => + this.handleCipherUpdateRepromptResponse(message), bgUnlockPopoutOpened: ({ message, sender }) => this.unlockVault(message, sender.tab), checkNotificationQueue: ({ sender }) => this.checkNotificationQueue(sender.tab), collectPageDetailsResponse: ({ message }) => @@ -631,6 +634,17 @@ export default class NotificationBackground { await this.saveOrUpdateCredentials(sender.tab, message.edit, message.folder); } + async handleCipherUpdateRepromptResponse(message: NotificationBackgroundExtensionMessage) { + if (message.success) { + await this.saveOrUpdateCredentials(message.tab, false, undefined, true); + } else { + await BrowserApi.tabSendMessageData(message.tab, "saveCipherAttemptCompleted", { + error: "Password reprompt failed", + }); + return; + } + } + /** * Saves or updates credentials based on the message within the * notification queue that is associated with the specified tab. @@ -639,7 +653,12 @@ export default class NotificationBackground { * @param edit - Identifies if the credentials should be edited or simply added * @param folderId - The folder to add the cipher to */ - private async saveOrUpdateCredentials(tab: chrome.tabs.Tab, edit: boolean, folderId?: string) { + private async saveOrUpdateCredentials( + tab: chrome.tabs.Tab, + edit: boolean, + folderId?: string, + skipReprompt: boolean = false, + ) { for (let i = this.notificationQueue.length - 1; i >= 0; i--) { const queueMessage = this.notificationQueue[i]; if ( @@ -654,18 +673,26 @@ export default class NotificationBackground { continue; } - this.notificationQueue.splice(i, 1); - const activeUserId = await firstValueFrom( this.accountService.activeAccount$.pipe(getOptionalUserId), ); if (queueMessage.type === NotificationQueueMessageType.ChangePassword) { const cipherView = await this.getDecryptedCipherById(queueMessage.cipherId, activeUserId); - await this.updatePassword(cipherView, queueMessage.newPassword, edit, tab, activeUserId); + + await this.updatePassword( + cipherView, + queueMessage.newPassword, + edit, + tab, + activeUserId, + skipReprompt, + ); return; } + this.notificationQueue.splice(i, 1); + // If the vault was locked, check if a cipher needs updating instead of creating a new one if (queueMessage.wasVaultLocked) { const allCiphers = await this.cipherService.getAllDecryptedForUrl( @@ -725,6 +752,7 @@ export default class NotificationBackground { edit: boolean, tab: chrome.tabs.Tab, userId: UserId, + skipReprompt: boolean = false, ) { cipherView.login.password = newPassword; @@ -758,6 +786,12 @@ export default class NotificationBackground { } : undefined; + if (cipherView.reprompt && !skipReprompt) { + await this.autofillService.isPasswordRepromptRequired(cipherView, tab, UPDATE_PASSWORD); + + return; + } + await this.cipherService.updateWithServer(cipher); await BrowserApi.tabSendMessageData(tab, "saveCipherAttemptCompleted", { diff --git a/apps/browser/src/autofill/overlay/notifications/abstractions/overlay-notifications-content.service.ts b/apps/browser/src/autofill/overlay/notifications/abstractions/overlay-notifications-content.service.ts index 82c03cacadf..42d7666e8a7 100644 --- a/apps/browser/src/autofill/overlay/notifications/abstractions/overlay-notifications-content.service.ts +++ b/apps/browser/src/autofill/overlay/notifications/abstractions/overlay-notifications-content.service.ts @@ -15,6 +15,7 @@ export type NotificationsExtensionMessage = { typeData?: NotificationTypeData; height?: number; error?: string; + closedByUser?: boolean; fadeOutNotification?: boolean; }; }; diff --git a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts index 519521feaa9..c21aaa37dd4 100644 --- a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts +++ b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts @@ -106,13 +106,15 @@ export class OverlayNotificationsContentService * @param message - The message containing the data for closing the notification bar. */ private handleCloseNotificationBarMessage(message: NotificationsExtensionMessage) { + const closedByUser = + typeof message.data?.closedByUser === "boolean" ? message.data.closedByUser : true; if (message.data?.fadeOutNotification) { setElementStyles(this.notificationBarIframeElement, { opacity: "0" }, true); - globalThis.setTimeout(() => this.closeNotificationBar(true), 150); + globalThis.setTimeout(() => this.closeNotificationBar(closedByUser), 150); return; } - this.closeNotificationBar(true); + this.closeNotificationBar(closedByUser); } /** diff --git a/apps/browser/src/autofill/services/abstractions/autofill.service.ts b/apps/browser/src/autofill/services/abstractions/autofill.service.ts index 5b1b4b3b8bb..daafd871789 100644 --- a/apps/browser/src/autofill/services/abstractions/autofill.service.ts +++ b/apps/browser/src/autofill/services/abstractions/autofill.service.ts @@ -87,5 +87,9 @@ export abstract class AutofillService { cipherType?: CipherType, ) => Promise; setAutoFillOnPageLoadOrgPolicy: () => Promise; - isPasswordRepromptRequired: (cipher: CipherView, tab: chrome.tabs.Tab) => Promise; + isPasswordRepromptRequired: ( + cipher: CipherView, + tab: chrome.tabs.Tab, + action?: string, + ) => Promise; } diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index da46ceb0864..525010bacc1 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -593,15 +593,20 @@ export default class AutofillService implements AutofillServiceInterface { * * @param cipher - The cipher to autofill * @param tab - The tab to autofill + * @param action - override for default action once reprompt is completed successfully */ - async isPasswordRepromptRequired(cipher: CipherView, tab: chrome.tabs.Tab): Promise { + async isPasswordRepromptRequired( + cipher: CipherView, + tab: chrome.tabs.Tab, + action?: string, + ): Promise { const userHasMasterPasswordAndKeyHash = await this.userVerificationService.hasMasterPasswordAndMasterKeyHash(); if (cipher.reprompt === CipherRepromptType.Password && userHasMasterPasswordAndKeyHash) { if (!this.isDebouncingPasswordRepromptPopout()) { await this.openVaultItemPasswordRepromptPopout(tab, { cipherId: cipher.id, - action: "autofill", + action: action ?? "autofill", }); } diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts index 56db47619b0..a834314560b 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts @@ -19,6 +19,7 @@ import { COPY_USERNAME_ID, COPY_VERIFICATION_CODE_ID, SHOW_AUTOFILL_BUTTON, + UPDATE_PASSWORD, } from "@bitwarden/common/autofill/constants"; import { EventType } from "@bitwarden/common/enums"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -49,6 +50,7 @@ import { PasswordRepromptService, } from "@bitwarden/vault"; +import { sendExtensionMessage } from "../../../../../autofill/utils/index"; import { BrowserApi } from "../../../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils"; import { PopOutComponent } from "../../../../../platform/popup/components/pop-out.component"; @@ -72,7 +74,8 @@ type LoadAction = | typeof SHOW_AUTOFILL_BUTTON | typeof COPY_USERNAME_ID | typeof COPY_PASSWORD_ID - | typeof COPY_VERIFICATION_CODE_ID; + | typeof COPY_VERIFICATION_CODE_ID + | typeof UPDATE_PASSWORD; @Component({ selector: "app-view-v2", @@ -294,7 +297,7 @@ export class ViewV2Component { // Both vaultPopupAutofillService and copyCipherFieldService will perform password re-prompting internally. switch (loadAction) { - case "show-autofill-button": + case SHOW_AUTOFILL_BUTTON: // This action simply shows the cipher view, no need to do anything. if ( this.cipher.reprompt !== CipherRepromptType.None && @@ -303,30 +306,42 @@ export class ViewV2Component { await closeViewVaultItemPopout(`${VaultPopoutType.viewVaultItem}_${this.cipher.id}`); } return; - case "autofill": + case AUTOFILL_ID: actionSuccess = await this.vaultPopupAutofillService.doAutofill(this.cipher, false); break; - case "copy-username": + case COPY_USERNAME_ID: actionSuccess = await this.copyCipherFieldService.copy( this.cipher.login.username, "username", this.cipher, ); break; - case "copy-password": + case COPY_PASSWORD_ID: actionSuccess = await this.copyCipherFieldService.copy( this.cipher.login.password, "password", this.cipher, ); break; - case "copy-totp": + case COPY_VERIFICATION_CODE_ID: actionSuccess = await this.copyCipherFieldService.copy( this.cipher.login.totp, "totp", this.cipher, ); break; + case UPDATE_PASSWORD: { + const repromptSuccess = await this.passwordRepromptService.showPasswordPrompt(); + + await sendExtensionMessage("bgHandleReprompt", { + tab: await chrome.tabs.get(senderTabId), + success: repromptSuccess, + }); + + await closeViewVaultItemPopout(`${VaultPopoutType.viewVaultItem}_${this.cipher.id}`); + + break; + } } if (BrowserPopupUtils.inPopout(window)) { diff --git a/libs/common/src/autofill/constants/index.ts b/libs/common/src/autofill/constants/index.ts index 62ad10e9a90..dc79e27b6aa 100644 --- a/libs/common/src/autofill/constants/index.ts +++ b/libs/common/src/autofill/constants/index.ts @@ -38,7 +38,7 @@ export const ClearClipboardDelay = { FiveMinutes: 300, } as const; -/* Context Menu item Ids */ +/* Ids for context menu items and messaging events */ export const AUTOFILL_CARD_ID = "autofill-card"; export const AUTOFILL_ID = "autofill"; export const SHOW_AUTOFILL_BUTTON = "show-autofill-button"; @@ -54,6 +54,7 @@ export const GENERATE_PASSWORD_ID = "generate-password"; export const NOOP_COMMAND_SUFFIX = "noop"; export const ROOT_ID = "root"; export const SEPARATOR_ID = "separator"; +export const UPDATE_PASSWORD = "update-password"; export const NOTIFICATION_BAR_LIFESPAN_MS = 150000; // 150 seconds From 355bddc6c9d5c110e55fe74c5fcfa86ddd85572c Mon Sep 17 00:00:00 2001 From: Bryan Cunningham Date: Mon, 12 May 2025 11:50:29 -0400 Subject: [PATCH 21/44] [CL-301] Enable Storybook test runner. Run a11y tests also (#14646) * Enable Storybook test runner. Run a11y tests also * no need to return checkA11y function * add back decorator removed in error * add test runner to our ownership * add axe-playwright to our ownership --- .github/renovate.json5 | 2 + .storybook/preview.tsx | 7 + .storybook/test-runner.ts | 50 + package-lock.json | 2149 ++++++++++++++++++++++++++++++++++--- package.json | 4 + tsconfig.eslint.json | 1 + 6 files changed, 2062 insertions(+), 151 deletions(-) create mode 100644 .storybook/test-runner.ts diff --git a/.github/renovate.json5 b/.github/renovate.json5 index c1045739506..e5cd47077fb 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -312,6 +312,7 @@ "@angular/platform-browser", "@angular/platform", "@angular/router", + "axe-playwright", "@compodoc/compodoc", "@ng-select/ng-select", "@storybook/addon-a11y", @@ -320,6 +321,7 @@ "@storybook/addon-essentials", "@storybook/addon-interactions", "@storybook/addon-links", + "@storybook/test-runner", "@storybook/addon-themes", "@storybook/angular", "@storybook/manager-api", diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 6bd28cfe809..19a4fc51dc8 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -26,6 +26,13 @@ const preview: Preview = { wrapperDecorator, ], parameters: { + a11y: { + element: "#storybook-root", + manual: true, + options: { + runOnly: ["section508", "wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"], + }, + }, controls: { matchers: { color: /(background|color)$/i, diff --git a/.storybook/test-runner.ts b/.storybook/test-runner.ts new file mode 100644 index 00000000000..8584a321090 --- /dev/null +++ b/.storybook/test-runner.ts @@ -0,0 +1,50 @@ +import { + // waitForPageReady, + type TestRunnerConfig, +} from "@storybook/test-runner"; +import { injectAxe, checkA11y } from "axe-playwright"; + +const testRunnerConfig: TestRunnerConfig = { + setup() {}, + + async preVisit(page, context) { + return await injectAxe(page); + }, + + async postVisit(page, context) { + await page.waitForSelector("#storybook-root"); + // https://github.com/abhinaba-ghosh/axe-playwright#parameters-on-checka11y-axerun + await checkA11y( + // Playwright page instance. + page, + + // context + "#storybook-root", + + // axeOptions, see https://www.deque.com/axe/core-documentation/api-documentation/#parameters-axerun + { + detailedReport: true, + detailedReportOptions: { + // Includes the full html for invalid nodes + html: true, + }, + verbose: false, + }, + + // skipFailures + false, + + // reporter "v2" is terminal reporter, "html" writes results to file + "v2", + + // axeHtmlReporterOptions + // NOTE: set reporter param (above) to "html" to activate these options + { + outputDir: "reports/a11y", + reportFileName: `${context.id}.html`, + }, + ); + }, +}; + +export default testRunnerConfig; diff --git a/package-lock.json b/package-lock.json index a15275a695e..81c9384fbbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -91,6 +91,7 @@ "@storybook/addon-essentials": "8.6.12", "@storybook/addon-interactions": "8.6.12", "@storybook/addon-links": "8.6.12", + "@storybook/test-runner": "0.22.0", "@storybook/addon-themes": "8.6.12", "@storybook/angular": "8.6.12", "@storybook/manager-api": "8.6.12", @@ -123,6 +124,7 @@ "@yao-pkg/pkg": "5.16.1", "angular-eslint": "18.4.3", "autoprefixer": "10.4.21", + "axe-playwright": "2.1.0", "babel-loader": "9.2.1", "base64-loader": "1.0.0", "browserslist": "4.23.2", @@ -3327,7 +3329,6 @@ "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -3453,7 +3454,6 @@ "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -3580,7 +3580,6 @@ "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -4714,8 +4713,7 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@bitwarden/admin-console": { "resolved": "libs/admin-console", @@ -7179,7 +7177,6 @@ "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", @@ -7197,7 +7194,6 @@ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -7208,7 +7204,6 @@ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -7223,7 +7218,6 @@ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -7238,7 +7232,6 @@ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -7252,7 +7245,6 @@ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-try": "^2.0.0" }, @@ -7269,7 +7261,6 @@ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -7283,7 +7274,6 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -7293,8 +7283,7 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", @@ -7312,7 +7301,6 @@ "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -7331,7 +7319,6 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -7342,7 +7329,6 @@ "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/reporters": "^29.7.0", @@ -7391,7 +7377,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -7405,7 +7390,6 @@ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -7420,8 +7404,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@jest/core/node_modules/slash": { "version": "3.0.0", @@ -7429,11 +7412,23 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } }, + "node_modules/@jest/create-cache-key-function": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", @@ -7456,7 +7451,6 @@ "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "expect": "^29.7.0", "jest-snapshot": "^29.7.0" @@ -7502,7 +7496,6 @@ "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -7519,7 +7512,6 @@ "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^29.7.0", @@ -7564,7 +7556,6 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7577,7 +7568,6 @@ "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -7599,7 +7589,6 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7613,7 +7602,6 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -7637,7 +7625,6 @@ "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", @@ -7653,7 +7640,6 @@ "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", @@ -7670,7 +7656,6 @@ "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", @@ -7687,7 +7672,6 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -7698,7 +7682,6 @@ "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -7725,8 +7708,7 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@jest/transform/node_modules/slash": { "version": "3.0.0", @@ -7734,7 +7716,6 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -10819,6 +10800,43 @@ "storybook": "^8.6.12" } }, + "node_modules/@storybook/test-runner": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@storybook/test-runner/-/test-runner-0.22.0.tgz", + "integrity": "sha512-fKY6MTE/bcvMaulKXy+z0fPmRXJx1REkYMOMcGn8zn6uffyBigGgaVf/sZ+AZfibwvjzg/StWhJ9HvAM8pc14g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5", + "@jest/types": "^29.6.3", + "@storybook/csf": "^0.1.11", + "@swc/core": "^1.5.22", + "@swc/jest": "^0.2.23", + "expect-playwright": "^0.8.0", + "jest": "^29.6.4", + "jest-circus": "^29.6.4", + "jest-environment-node": "^29.6.4", + "jest-junit": "^16.0.0", + "jest-playwright-preset": "^4.0.0", + "jest-runner": "^29.6.4", + "jest-serializer-html": "^7.1.0", + "jest-watch-typeahead": "^2.0.0", + "nyc": "^15.1.0", + "playwright": "^1.14.0" + }, + "bin": { + "test-storybook": "dist/test-storybook.js" + }, + "engines": { + "node": "^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "storybook": "^0.0.0-0 || ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 || ^9.0.0-0" + } + }, "node_modules/@storybook/theming": { "version": "8.6.12", "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.6.12.tgz", @@ -10882,6 +10900,250 @@ "storybook": "^8.6.12" } }, + "node_modules/@swc/core": { + "version": "1.11.24", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.24.tgz", + "integrity": "sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.21" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.11.24", + "@swc/core-darwin-x64": "1.11.24", + "@swc/core-linux-arm-gnueabihf": "1.11.24", + "@swc/core-linux-arm64-gnu": "1.11.24", + "@swc/core-linux-arm64-musl": "1.11.24", + "@swc/core-linux-x64-gnu": "1.11.24", + "@swc/core-linux-x64-musl": "1.11.24", + "@swc/core-win32-arm64-msvc": "1.11.24", + "@swc/core-win32-ia32-msvc": "1.11.24", + "@swc/core-win32-x64-msvc": "1.11.24" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.11.24", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.24.tgz", + "integrity": "sha512-dhtVj0PC1APOF4fl5qT2neGjRLgHAAYfiVP8poJelhzhB/318bO+QCFWAiimcDoyMgpCXOhTp757gnoJJrheWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.11.24", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.24.tgz", + "integrity": "sha512-H/3cPs8uxcj2Fe3SoLlofN5JG6Ny5bl8DuZ6Yc2wr7gQFBmyBkbZEz+sPVgsID7IXuz7vTP95kMm1VL74SO5AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.11.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.24.tgz", + "integrity": "sha512-PHJgWEpCsLo/NGj+A2lXZ2mgGjsr96ULNW3+T3Bj2KTc8XtMUkE8tmY2Da20ItZOvPNC/69KroU7edyo1Flfbw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.11.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.24.tgz", + "integrity": "sha512-C2FJb08+n5SD4CYWCTZx1uR88BN41ZieoHvI8A55hfVf2woT8+6ZiBzt74qW2g+ntZ535Jts5VwXAKdu41HpBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.11.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.24.tgz", + "integrity": "sha512-ypXLIdszRo0re7PNNaXN0+2lD454G8l9LPK/rbfRXnhLWDBPURxzKlLlU/YGd2zP98wPcVooMmegRSNOKfvErw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.11.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.24.tgz", + "integrity": "sha512-IM7d+STVZD48zxcgo69L0yYptfhaaE9cMZ+9OoMxirNafhKKXwoZuufol1+alEFKc+Wbwp+aUPe/DeWC/Lh3dg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.11.24", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.24.tgz", + "integrity": "sha512-DZByJaMVzSfjQKKQn3cqSeqwy6lpMaQDQQ4HPlch9FWtDx/dLcpdIhxssqZXcR2rhaQVIaRQsCqwV6orSDGAGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.11.24", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.24.tgz", + "integrity": "sha512-Q64Ytn23y9aVDKN5iryFi8mRgyHw3/kyjTjT4qFCa8AEb5sGUuSj//AUZ6c0J7hQKMHlg9do5Etvoe61V98/JQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.11.24", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.24.tgz", + "integrity": "sha512-9pKLIisE/Hh2vJhGIPvSoTK4uBSPxNVyXHmOrtdDot4E1FUUI74Vi8tFdlwNbaj8/vusVnb8xPXsxF1uB0VgiQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.11.24", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.24.tgz", + "integrity": "sha512-sybnXtOsdB+XvzVFlBVGgRHLqp3yRpHK7CrmpuDKszhj/QhmsaZzY/GHSeALlMtLup13M0gqbcQvsTNlAHTg3w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@swc/jest": { + "version": "0.2.38", + "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.38.tgz", + "integrity": "sha512-HMoZgXWMqChJwffdDjvplH53g9G2ALQes3HKXDEdliB/b85OQ0CTSbxG8VSeCwiAn7cOaDVEt4mwmZvbHcS52w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/create-cache-key-function": "^29.7.0", + "@swc/counter": "^0.1.3", + "jsonc-parser": "^3.2.0" + }, + "engines": { + "npm": ">= 7.0.0" + }, + "peerDependencies": { + "@swc/core": "*" + } + }, + "node_modules/@swc/types": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.21.tgz", + "integrity": "sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -11102,7 +11364,6 @@ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -11117,7 +11378,6 @@ "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/types": "^7.0.0" } @@ -11128,7 +11388,6 @@ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" @@ -11140,7 +11399,6 @@ "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/types": "^7.20.7" } @@ -11336,7 +11594,6 @@ "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*" } @@ -11506,6 +11763,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/junit-report-builder": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/junit-report-builder/-/junit-report-builder-3.0.2.tgz", + "integrity": "sha512-R5M+SYhMbwBeQcNXYWNCZkl09vkVfAtcPIaCGdzIkkbeaTrVbGQ7HVgi4s+EmM/M1K4ZuWQH0jGcvMvNePfxYA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/keygrip": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz", @@ -11908,6 +12172,16 @@ "license": "MIT", "optional": true }, + "node_modules/@types/wait-on": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/@types/wait-on/-/wait-on-5.3.4.tgz", + "integrity": "sha512-EBsPjFMrFlMbbUFf9D1Fp+PAB2TwmUn7a3YtHyD9RLuTIk1jDd8SxXVAoez2Ciy+8Jsceo2MYEYZzJ/DvorOKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/webpack-env": { "version": "1.18.8", "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.18.8.tgz", @@ -13845,6 +14119,19 @@ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "license": "MIT" }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/archiver": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", @@ -13981,6 +14268,13 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true, + "license": "MIT" + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -14356,6 +14650,39 @@ "node": ">=4" } }, + "node_modules/axe-html-reporter": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/axe-html-reporter/-/axe-html-reporter-2.2.11.tgz", + "integrity": "sha512-WlF+xlNVgNVWiM6IdVrsh+N0Cw7qupe5HT9N6Uyi+aN7f6SSi92RDomiP1noW8OWIV85V6x404m5oKMeqRV3tQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mustache": "^4.0.1" + }, + "engines": { + "node": ">=8.9.0" + }, + "peerDependencies": { + "axe-core": ">=3" + } + }, + "node_modules/axe-playwright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/axe-playwright/-/axe-playwright-2.1.0.tgz", + "integrity": "sha512-tY48SX56XaAp16oHPyD4DXpybz8Jxdz9P7exTjF/4AV70EGUavk+1fUPWirM0OYBR+YyDx6hUeDvuHVA6fB9YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/junit-report-builder": "^3.0.2", + "axe-core": "^4.10.1", + "axe-html-reporter": "2.2.11", + "junit-report-builder": "^5.1.1", + "picocolors": "^1.1.1" + }, + "peerDependencies": { + "playwright": ">1.0.0" + } + }, "node_modules/axios": { "version": "1.8.4", "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", @@ -14383,7 +14710,6 @@ "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", @@ -14406,7 +14732,6 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -14435,7 +14760,6 @@ "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", @@ -14453,7 +14777,6 @@ "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", @@ -14471,7 +14794,6 @@ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" } @@ -14482,7 +14804,6 @@ "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", @@ -14600,7 +14921,6 @@ "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", @@ -14628,7 +14948,6 @@ "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" @@ -15090,7 +15409,6 @@ "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "node-int64": "^0.4.0" } @@ -15471,6 +15789,68 @@ "node": ">=8" } }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/caching-transform/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caching-transform/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/caching-transform/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/caching-transform/node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -15553,7 +15933,6 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -15670,7 +16049,6 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -16037,8 +16415,7 @@ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/color-convert": { "version": "2.0.1", @@ -16123,6 +16500,13 @@ "node": ">=4.0.0" } }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, "node_modules/compare-version": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", @@ -16751,7 +17135,6 @@ "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", @@ -16942,6 +17325,20 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/cwd": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/cwd/-/cwd-0.10.0.tgz", + "integrity": "sha512-YGZxdTTL9lmLkCUTpg4j0zQ7IhRB5ZmqNBbGCl3Tg6MP/d5/6sY7L5mmTjzbc6JKgVZYiqTQTNhPFsbXNGlRaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-pkg": "^0.1.2", + "fs-exists-sync": "^0.1.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/data-urls": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", @@ -17120,7 +17517,6 @@ "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, @@ -17216,6 +17612,22 @@ "node": ">= 10" } }, + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -17342,7 +17754,6 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -17385,6 +17796,100 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/diffable-html": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/diffable-html/-/diffable-html-4.1.0.tgz", + "integrity": "sha512-++kyNek+YBLH8cLXS+iTj/Hiy2s5qkRJEJ8kgu/WHbFrVY2vz9xPFUT+fii2zGF0m1CaojDlQJjkfrCt7YWM1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "htmlparser2": "^3.9.2" + } + }, + "node_modules/diffable-html/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/diffable-html/node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/diffable-html/node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/diffable-html/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/diffable-html/node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/diffable-html/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/diffable-html/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/diffable-html/node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, "node_modules/dir-compare": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", @@ -18057,7 +18562,6 @@ "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -18382,8 +18886,7 @@ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/es6-shim": { "version": "0.35.8", @@ -19185,7 +19688,6 @@ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, - "peer": true, "engines": { "node": ">= 0.8.0" } @@ -19200,6 +19702,19 @@ "node": ">=6" } }, + "node_modules/expand-tilde": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", + "integrity": "sha512-rtmc+cjLZqnu9dSYosX9EWmSJhTwpACgJQTfj4hgg2JjOD/6SIQalZrt4a3aQeh++oNxkazcaxrhPUj6+g5G/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-homedir": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", @@ -19217,6 +19732,24 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/expect-playwright": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/expect-playwright/-/expect-playwright-0.8.0.tgz", + "integrity": "sha512-+kn8561vHAY+dt+0gMqqj1oY+g5xWrsuGMk4QGxotT2WS545nVqqjs37z6hrYfIuucwqthzwJfCJUEYqixyljg==", + "dev": true, + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/exponential-backoff": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", @@ -19596,7 +20129,6 @@ "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "bser": "2.1.1" } @@ -19771,6 +20303,58 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-file-up": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-0.1.3.tgz", + "integrity": "sha512-mBxmNbVyjg1LQIIpgO8hN+ybWBgDQK8qjht+EbrTCGmmPV/sc7RF1i9stPTD6bpvXZywBdrwRYxhSdJv867L6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-exists-sync": "^0.1.0", + "resolve-dir": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-pkg": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/find-pkg/-/find-pkg-0.1.2.tgz", + "integrity": "sha512-0rnQWcFwZr7eO0513HahrWafsc3CTFioEB7DRiEYCUM/70QXSY8f3mCST17HXLcPvEhzH/Ty/Bxd72ZZsr/yvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-file-up": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-process": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/find-process/-/find-process-1.4.10.tgz", + "integrity": "sha512-ncYFnWEIwL7PzmrK1yZtaccN8GhethD37RzBHG6iOZoFYB4vSmLLXfeWJjeN5nMvCJMjOtBvBBF8OgxEcikiZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "~4.1.2", + "commander": "^12.1.0", + "loglevel": "^1.9.2" + }, + "bin": { + "find-process": "bin/find-process.js" + } + }, + "node_modules/find-process/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -20305,6 +20889,27 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/front-matter": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", @@ -20349,6 +20954,16 @@ "dev": true, "license": "MIT" }, + "node_modules/fs-exists-sync": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", + "integrity": "sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fs-extra": { "version": "11.3.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", @@ -20527,7 +21142,6 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -20678,6 +21292,56 @@ "node": ">=10.0" } }, + "node_modules/global-modules": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", + "integrity": "sha512-JeXuCbvYzYXcwE6acL9V2bAOeSIGl4dD+iwLY9iUx2VBJJ80R18HCn+JCwHM9Oegdfya3lEkGCdaRkSyc10hDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^0.1.4", + "is-windows": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", + "integrity": "sha512-gOPiyxcD9dJGCEArAhF4Hd0BAqvAe/JzERP7tYumE4yIkmIedPUVXcJFWbV3/p/ovIIvKjkrTk+f1UVkq7vvbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.0", + "ini": "^1.3.4", + "is-windows": "^0.2.0", + "which": "^1.2.12" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -20942,6 +21606,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -20964,6 +21655,19 @@ "he": "bin/he" } }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -21084,8 +21788,7 @@ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/html-loader": { "version": "5.1.0", @@ -22170,7 +22873,6 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -22475,6 +23177,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -22540,6 +23249,16 @@ "dev": true, "license": "MIT" }, + "node_modules/is-windows": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", + "integrity": "sha512-n67eJYmXbniZB7RF4I/FTjK1s6RPOCTxhYrVYLRaCt3lF0mpWZPKr3T2LSZAqyjQsxR2qMmGYXXzK0YWwcPM1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -22597,6 +23316,19 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/istanbul-lib-instrument": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", @@ -22614,13 +23346,116 @@ "node": ">=10" } }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "license": "ISC", + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", @@ -22636,7 +23471,6 @@ "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", @@ -22652,7 +23486,6 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -22663,7 +23496,6 @@ "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -22737,7 +23569,6 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -22765,7 +23596,6 @@ "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "execa": "^5.0.0", "jest-util": "^29.7.0", @@ -22781,7 +23611,6 @@ "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -22814,7 +23643,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -22828,7 +23656,6 @@ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -22843,8 +23670,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-circus/node_modules/slash": { "version": "3.0.0", @@ -22852,7 +23678,6 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -22863,7 +23688,6 @@ "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -22898,7 +23722,6 @@ "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^29.7.0", @@ -22945,7 +23768,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -22959,7 +23781,6 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -22972,7 +23793,6 @@ "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -22994,7 +23814,6 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -23008,7 +23827,6 @@ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -23023,8 +23841,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-config/node_modules/slash": { "version": "3.0.0", @@ -23032,7 +23849,6 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -23094,7 +23910,6 @@ "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "detect-newline": "^3.0.0" }, @@ -23108,7 +23923,6 @@ "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", @@ -23126,7 +23940,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -23140,7 +23953,6 @@ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -23155,8 +23967,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-environment-jsdom": { "version": "29.7.0", @@ -23385,7 +24196,6 @@ "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -23414,7 +24224,6 @@ "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -23467,7 +24276,6 @@ "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" @@ -23482,7 +24290,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -23496,7 +24303,6 @@ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -23511,8 +24317,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-matcher-utils": { "version": "29.7.0", @@ -23660,13 +24465,106 @@ "typescript": "^3.0.0 || ^4.0.0 || ^5.0.0" } }, + "node_modules/jest-playwright-preset": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jest-playwright-preset/-/jest-playwright-preset-4.0.0.tgz", + "integrity": "sha512-+dGZ1X2KqtwXaabVjTGxy0a3VzYfvYsWaRcuO8vMhyclHSOpGSI1+5cmlqzzCwQ3+fv0EjkTc7I5aV9lo08dYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect-playwright": "^0.8.0", + "jest-process-manager": "^0.4.0", + "nyc": "^15.1.0", + "playwright-core": ">=1.2.0", + "rimraf": "^3.0.2", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "jest": "^29.3.1", + "jest-circus": "^29.3.1", + "jest-environment-node": "^29.3.1", + "jest-runner": "^29.3.1" + } + }, + "node_modules/jest-playwright-preset/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jest-playwright-preset/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-playwright-preset/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jest-playwright-preset/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-playwright-preset/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/jest-pnp-resolver": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" }, @@ -23743,13 +24641,58 @@ "dev": true, "license": "MIT" }, + "node_modules/jest-process-manager": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/jest-process-manager/-/jest-process-manager-0.4.0.tgz", + "integrity": "sha512-80Y6snDyb0p8GG83pDxGI/kQzwVTkCxc7ep5FPe/F6JYdvRDhwr6RzRmPSP7SEwuLhxo80lBS/NqOdUIbHIfhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/wait-on": "^5.2.0", + "chalk": "^4.1.0", + "cwd": "^0.10.0", + "exit": "^0.1.2", + "find-process": "^1.4.4", + "prompts": "^2.4.1", + "signal-exit": "^3.0.3", + "spawnd": "^5.0.0", + "tree-kill": "^1.2.2", + "wait-on": "^7.0.0" + } + }, + "node_modules/jest-process-manager/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/jest-process-manager/node_modules/wait-on": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz", + "integrity": "sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "^1.6.1", + "joi": "^17.11.0", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "rxjs": "^7.8.1" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/jest-regex-util": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -23760,7 +24703,6 @@ "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -23782,7 +24724,6 @@ "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "jest-regex-util": "^29.6.3", "jest-snapshot": "^29.7.0" @@ -23797,7 +24738,6 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -23808,7 +24748,6 @@ "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/environment": "^29.7.0", @@ -23842,7 +24781,6 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -23853,7 +24791,6 @@ "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -23865,7 +24802,6 @@ "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -23900,7 +24836,6 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -23913,7 +24848,6 @@ "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -23935,7 +24869,6 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -23949,18 +24882,26 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } }, + "node_modules/jest-serializer-html": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer-html/-/jest-serializer-html-7.1.0.tgz", + "integrity": "sha512-xYL2qC7kmoYHJo8MYqJkzrl/Fdlx+fat4U1AqYg+kafqwcKPiMkOcjWHPKhueuNEgr+uemhGc+jqXYiwCyRyLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "diffable-html": "^4.1.0" + } + }, "node_modules/jest-snapshot": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", @@ -23993,7 +24934,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -24007,7 +24947,6 @@ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -24022,8 +24961,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-util": { "version": "29.7.0", @@ -24062,7 +25000,6 @@ "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -24081,7 +25018,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -24095,7 +25031,6 @@ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -24109,7 +25044,6 @@ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -24124,8 +25058,111 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, + "license": "MIT" + }, + "node_modules/jest-watch-typeahead": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-2.2.2.tgz", + "integrity": "sha512-+QgOFW4o5Xlgd6jGS5X37i08tuuXNW8X0CV9WNFi+3n8ExCIP+E1melYhvYLjv5fE6D0yyzk74vsSO8I6GqtvQ==", + "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "ansi-escapes": "^6.0.0", + "chalk": "^5.2.0", + "jest-regex-util": "^29.0.0", + "jest-watcher": "^29.0.0", + "slash": "^5.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "jest": "^27.0.0 || ^28.0.0 || ^29.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/ansi-escapes": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watch-typeahead/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/char-regex": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.2.tgz", + "integrity": "sha512-cbGOjAptfM2LVmWhwRFHEKTPkLwNddVmuqYZQt895yXwAsWsXObCG+YN4DGQ/JBtT4GP1a1lPPdio2z413LmTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/jest-watch-typeahead/node_modules/string-length": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watch-typeahead/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } }, "node_modules/jest-watcher": { "version": "29.7.0", @@ -24133,7 +25170,6 @@ "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", @@ -24154,7 +25190,6 @@ "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -24171,7 +25206,6 @@ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -24518,6 +25552,47 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/junit-report-builder": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/junit-report-builder/-/junit-report-builder-5.1.1.tgz", + "integrity": "sha512-ZNOIIGMzqCGcHQEA2Q4rIQQ3Df6gSIfne+X9Rly9Bc2y55KxAZu8iGv+n2pP0bLf0XAOctJZgeloC54hWzCahQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21", + "make-dir": "^3.1.0", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/junit-report-builder/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/junit-report-builder/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/jwt-decode": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", @@ -24897,7 +25972,6 @@ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -25602,6 +26676,13 @@ "license": "MIT", "peer": true }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -26001,7 +27082,6 @@ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "semver": "^7.5.3" }, @@ -26097,7 +27177,6 @@ "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "tmpl": "1.0.5" } @@ -27587,6 +28666,16 @@ "readable-stream": "^3.6.0" } }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "dev": true, + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/mute-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", @@ -28164,8 +29253,7 @@ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/node-machine-id": { "version": "1.1.12", @@ -28173,6 +29261,19 @@ "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", "dev": true }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -28844,6 +29945,350 @@ "node": ">=6" } }, + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/nyc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nyc/node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/nyc/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/nyc/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -29113,6 +30558,16 @@ "dev": true, "license": "MIT" }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/os-name": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", @@ -29270,6 +30725,22 @@ "node": ">=6" } }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -29551,6 +31022,16 @@ "node": ">= 0.10" } }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/parse5": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", @@ -30112,6 +31593,53 @@ "node": ">=4" } }, + "node_modules/playwright": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", + "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.52.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", + "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", @@ -30634,6 +32162,19 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/process-on-spawn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", + "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -30808,8 +32349,7 @@ "url": "https://opencollective.com/fast-check" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/qrcode-parser": { "version": "2.1.3", @@ -31308,6 +32848,19 @@ "node": ">= 0.10" } }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "license": "ISC", + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/remark-gfm": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", @@ -31487,6 +33040,13 @@ "node": ">=0.10.0" } }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true, + "license": "ISC" + }, "node_modules/requireindex": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", @@ -31550,6 +33110,20 @@ "node": ">=8" } }, + "node_modules/resolve-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", + "integrity": "sha512-QxMPqI6le2u0dCLyiGzgy92kjkkL6zO0XyvHzjdTNH3zM6e5Hz3BwG6+aEyNgiQ5Xz6PwTwgQEj3U50dByPKIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^1.2.2", + "global-modules": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -32444,6 +34018,13 @@ "node": ">= 0.8" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, "node_modules/set-cookie-parser": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", @@ -32888,6 +34469,164 @@ "node": ">=0.10.0" } }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/spawn-wrap/node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/spawn-wrap/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/spawn-wrap/node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-wrap/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/spawn-wrap/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/spawn-wrap/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/spawn-wrap/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/spawn-wrap/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/spawnd": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-5.0.0.tgz", + "integrity": "sha512-28+AJr82moMVWolQvlAIv3JcYDkjkFTEmfDc503wxrF5l2rQ3dFz6DpbXp3kD4zmgGGldfM4xM4v1sFj/ZaIOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "exit": "^0.1.2", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "wait-port": "^0.2.9" + } + }, + "node_modules/spawnd/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -33234,7 +34973,6 @@ "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" @@ -33364,7 +35102,6 @@ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -33927,7 +35664,6 @@ "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", @@ -33943,7 +35679,6 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -33956,7 +35691,6 @@ "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -33978,7 +35712,6 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -34148,8 +35881,7 @@ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -34899,6 +36631,16 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "license": "MIT" }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, "node_modules/typescript": { "version": "5.4.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", @@ -35490,7 +37232,6 @@ "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", @@ -35505,8 +37246,7 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/validate-npm-package-license": { "version": "3.0.4", @@ -36131,6 +37871,109 @@ "tslib": "^2.1.0" } }, + "node_modules/wait-port": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/wait-port/-/wait-port-0.2.14.tgz", + "integrity": "sha512-kIzjWcr6ykl7WFbZd0TMae8xovwqcqbx6FM9l+7agOgUByhzdjfzZBPK2CPufldTOMxbUivss//Sh9MFawmPRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "commander": "^3.0.2", + "debug": "^4.1.1" + }, + "bin": { + "wait-port": "bin/wait-port.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wait-port/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wait-port/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wait-port/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/wait-port/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/wait-port/node_modules/commander": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", + "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", + "dev": true, + "license": "MIT" + }, + "node_modules/wait-port/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/wait-port/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/wait-port/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/walk-up-path": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", @@ -36144,7 +37987,6 @@ "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "makeerror": "1.0.12" } @@ -36883,6 +38725,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, + "license": "ISC" + }, "node_modules/which-typed-array": { "version": "1.1.18", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", @@ -37030,7 +38879,6 @@ "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -37044,8 +38892,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/ws": { "version": "8.18.1", diff --git a/package.json b/package.json index 904ae121cc9..8ba1c95df6a 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "storybook": "ng run components:storybook", "build-storybook": "ng run components:build-storybook", "build-storybook:ci": "ng run components:build-storybook --webpack-stats-json", + "test-stories": "test-storybook --url http://localhost:6006", + "test-stories:watch": "test-stories --watch", "postinstall": "patch-package" }, "workspaces": [ @@ -53,6 +55,7 @@ "@storybook/addon-essentials": "8.6.12", "@storybook/addon-interactions": "8.6.12", "@storybook/addon-links": "8.6.12", + "@storybook/test-runner": "0.22.0", "@storybook/addon-themes": "8.6.12", "@storybook/angular": "8.6.12", "@storybook/manager-api": "8.6.12", @@ -85,6 +88,7 @@ "@yao-pkg/pkg": "5.16.1", "angular-eslint": "18.4.3", "autoprefixer": "10.4.21", + "axe-playwright": "2.1.0", "babel-loader": "9.2.1", "base64-loader": "1.0.0", "browserslist": "4.23.2", diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 7ffa34df58c..e8c3f669c0c 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -45,6 +45,7 @@ "files": [ ".storybook/main.ts", ".storybook/manager.js", + ".storybook/test-runner.ts", "apps/browser/src/autofill/content/components/.lit-storybook/main.ts" ], "include": ["apps/**/*", "libs/**/*", "bitwarden_license/**/*", "scripts/**/*"], From a10b9530af5628552583f8afb31cb0d9d6928fd2 Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Mon, 12 May 2025 18:20:56 +0100 Subject: [PATCH 22/44] changes to sponsorship is showing in individual vault (#14627) --- .../app/billing/settings/sponsored-families.component.ts | 8 +++++--- .../admin-console/models/data/organization.data.spec.ts | 1 + .../src/admin-console/models/data/organization.data.ts | 2 ++ .../src/admin-console/models/domain/organization.ts | 2 ++ .../models/response/profile-organization.response.ts | 2 ++ 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/billing/settings/sponsored-families.component.ts b/apps/web/src/app/billing/settings/sponsored-families.component.ts index cee08bae8cd..7e493168ce2 100644 --- a/apps/web/src/app/billing/settings/sponsored-families.component.ts +++ b/apps/web/src/app/billing/settings/sponsored-families.component.ts @@ -112,13 +112,15 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy { }); } }); - this.anyOrgsAvailable$ = this.availableSponsorshipOrgs$.pipe(map((orgs) => orgs.length > 0)); this.activeSponsorshipOrgs$ = this.organizationService .organizations$(userId) - .pipe(map((orgs) => orgs.filter((o) => o.familySponsorshipFriendlyName !== null))); - + .pipe( + map((orgs) => + orgs.filter((o) => o.familySponsorshipFriendlyName !== null && !o.isAdminInitiated), + ), + ); this.anyActiveSponsorships$ = this.activeSponsorshipOrgs$.pipe(map((orgs) => orgs.length > 0)); this.loading = false; diff --git a/libs/common/src/admin-console/models/data/organization.data.spec.ts b/libs/common/src/admin-console/models/data/organization.data.spec.ts index fae24133502..7d66e7bc0d5 100644 --- a/libs/common/src/admin-console/models/data/organization.data.spec.ts +++ b/libs/common/src/admin-console/models/data/organization.data.spec.ts @@ -59,6 +59,7 @@ describe("ORGANIZATIONS state", () => { userIsManagedByOrganization: false, useRiskInsights: false, useAdminSponsoredFamilies: false, + isAdminInitiated: false, }, }; const result = sut.deserializer(JSON.parse(JSON.stringify(expectedResult))); diff --git a/libs/common/src/admin-console/models/data/organization.data.ts b/libs/common/src/admin-console/models/data/organization.data.ts index 799d062aefa..e0783957117 100644 --- a/libs/common/src/admin-console/models/data/organization.data.ts +++ b/libs/common/src/admin-console/models/data/organization.data.ts @@ -61,6 +61,7 @@ export class OrganizationData { userIsManagedByOrganization: boolean; useRiskInsights: boolean; useAdminSponsoredFamilies: boolean; + isAdminInitiated: boolean; constructor( response?: ProfileOrganizationResponse, @@ -124,6 +125,7 @@ export class OrganizationData { this.userIsManagedByOrganization = response.userIsManagedByOrganization; this.useRiskInsights = response.useRiskInsights; this.useAdminSponsoredFamilies = response.useAdminSponsoredFamilies; + this.isAdminInitiated = response.isAdminInitiated; this.isMember = options.isMember; this.isProviderUser = options.isProviderUser; diff --git a/libs/common/src/admin-console/models/domain/organization.ts b/libs/common/src/admin-console/models/domain/organization.ts index 2e51c54b0ad..1864d56649b 100644 --- a/libs/common/src/admin-console/models/domain/organization.ts +++ b/libs/common/src/admin-console/models/domain/organization.ts @@ -91,6 +91,7 @@ export class Organization { userIsManagedByOrganization: boolean; useRiskInsights: boolean; useAdminSponsoredFamilies: boolean; + isAdminInitiated: boolean; constructor(obj?: OrganizationData) { if (obj == null) { @@ -150,6 +151,7 @@ export class Organization { this.userIsManagedByOrganization = obj.userIsManagedByOrganization; this.useRiskInsights = obj.useRiskInsights; this.useAdminSponsoredFamilies = obj.useAdminSponsoredFamilies; + this.isAdminInitiated = obj.isAdminInitiated; } get canAccess() { diff --git a/libs/common/src/admin-console/models/response/profile-organization.response.ts b/libs/common/src/admin-console/models/response/profile-organization.response.ts index da97a1034b1..3a86c764eb8 100644 --- a/libs/common/src/admin-console/models/response/profile-organization.response.ts +++ b/libs/common/src/admin-console/models/response/profile-organization.response.ts @@ -56,6 +56,7 @@ export class ProfileOrganizationResponse extends BaseResponse { userIsManagedByOrganization: boolean; useRiskInsights: boolean; useAdminSponsoredFamilies: boolean; + isAdminInitiated: boolean; constructor(response: any) { super(response); @@ -123,5 +124,6 @@ export class ProfileOrganizationResponse extends BaseResponse { this.userIsManagedByOrganization = this.getResponseProperty("UserIsManagedByOrganization"); this.useRiskInsights = this.getResponseProperty("UseRiskInsights"); this.useAdminSponsoredFamilies = this.getResponseProperty("UseAdminSponsoredFamilies"); + this.isAdminInitiated = this.getResponseProperty("IsAdminInitiated"); } } From eed18c929437ab418f35b02b866088a041a83683 Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Mon, 12 May 2025 14:26:52 -0400 Subject: [PATCH 23/44] chore(view-cache): [PM-21154] Move view-cache its own feature package and adjust imports * Moved view-cache services to directory * Fixed DI for browser extension. * Fixed tests. --- .../view-cache/popup-view-cache.service.ts | 2 +- .../src/popup/services/services.module.ts | 2 +- .../vault-popup-list-filters.service.spec.ts | 5 +- .../vault-popup-list-filters.service.ts | 5 +- libs/angular/src/platform/view-cache/index.ts | 1 + .../src/platform/view-cache/internal.ts | 1 + .../noop-view-cache.service.ts | 6 +- .../src/platform/view-cache/view-cache.md | 130 ++++++++++++++++++ .../view-cache.service.ts | 2 + .../src/services/jslib-services.module.ts | 5 +- ...auth-component-email-cache.service.spec.ts | 2 +- ...auth-email-component-cache.service.spec.ts | 2 +- ...ctor-auth-email-component-cache.service.ts | 2 +- ...actor-auth-component-cache.service.spec.ts | 2 +- ...two-factor-auth-component-cache.service.ts | 2 +- ...gin-via-auth-request-cache.service.spec.ts | 2 +- ...lt-login-via-auth-request-cache.service.ts | 2 +- .../src/cipher-form/cipher-form.stories.ts | 2 +- .../components/cipher-form.component.spec.ts | 2 +- .../default-cipher-form-cache.service.spec.ts | 2 +- .../default-cipher-form-cache.service.ts | 2 +- 21 files changed, 155 insertions(+), 26 deletions(-) create mode 100644 libs/angular/src/platform/view-cache/index.ts create mode 100644 libs/angular/src/platform/view-cache/internal.ts rename libs/angular/src/platform/{services => view-cache}/noop-view-cache.service.ts (87%) create mode 100644 libs/angular/src/platform/view-cache/view-cache.md rename libs/angular/src/platform/{abstractions => view-cache}/view-cache.service.ts (98%) diff --git a/apps/browser/src/platform/popup/view-cache/popup-view-cache.service.ts b/apps/browser/src/platform/popup/view-cache/popup-view-cache.service.ts index ff63b52ab3f..2a946982990 100644 --- a/apps/browser/src/platform/popup/view-cache/popup-view-cache.service.ts +++ b/apps/browser/src/platform/popup/view-cache/popup-view-cache.service.ts @@ -19,7 +19,7 @@ import { FormCacheOptions, SignalCacheOptions, ViewCacheService, -} from "@bitwarden/angular/platform/abstractions/view-cache.service"; +} from "@bitwarden/angular/platform/view-cache"; import { MessageSender } from "@bitwarden/common/platform/messaging"; import { GlobalStateProvider } from "@bitwarden/common/platform/state"; diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 00b8ae81cf9..6ede88dfc13 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -5,9 +5,9 @@ import { Router } from "@angular/router"; import { merge, of, Subject } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; -import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; import { AngularThemingService } from "@bitwarden/angular/platform/services/theming/angular-theming.service"; import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { CLIENT_TYPE, DEFAULT_VAULT_TIMEOUT, diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts index 9498d953808..621ec795157 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts @@ -4,6 +4,7 @@ import { FormBuilder } from "@angular/forms"; import { BehaviorSubject, skipWhile } from "rxjs"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -20,8 +21,6 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; -import { PopupViewCacheService } from "../../../platform/popup/view-cache/popup-view-cache.service"; - import { CachedFilterState, MY_VAULT_ID, @@ -123,7 +122,7 @@ describe("VaultPopupListFiltersService", () => { useValue: accountService, }, { - provide: PopupViewCacheService, + provide: ViewCacheService, useValue: viewCacheService, }, ], diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts index c726678c973..db4cfeefe9f 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts @@ -15,6 +15,7 @@ import { } from "rxjs"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { DynamicTreeNode } from "@bitwarden/angular/vault/vault-filter/models/dynamic-tree-node.model"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -40,8 +41,6 @@ import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { ServiceUtils } from "@bitwarden/common/vault/service-utils"; import { ChipSelectOption } from "@bitwarden/components"; -import { PopupViewCacheService } from "../../../platform/popup/view-cache/popup-view-cache.service"; - const FILTER_VISIBILITY_KEY = new KeyDefinition(VAULT_SETTINGS_DISK, "filterVisibility", { deserializer: (obj) => obj, }); @@ -178,7 +177,7 @@ export class VaultPopupListFiltersService { private policyService: PolicyService, private stateProvider: StateProvider, private accountService: AccountService, - private viewCacheService: PopupViewCacheService, + private viewCacheService: ViewCacheService, ) { this.filterForm.controls.organization.valueChanges .pipe(takeUntilDestroyed()) diff --git a/libs/angular/src/platform/view-cache/index.ts b/libs/angular/src/platform/view-cache/index.ts new file mode 100644 index 00000000000..79deef6aa5b --- /dev/null +++ b/libs/angular/src/platform/view-cache/index.ts @@ -0,0 +1 @@ +export { ViewCacheService, FormCacheOptions, SignalCacheOptions } from "./view-cache.service"; diff --git a/libs/angular/src/platform/view-cache/internal.ts b/libs/angular/src/platform/view-cache/internal.ts new file mode 100644 index 00000000000..6e0992eecbf --- /dev/null +++ b/libs/angular/src/platform/view-cache/internal.ts @@ -0,0 +1 @@ +export { NoopViewCacheService } from "./noop-view-cache.service"; diff --git a/libs/angular/src/platform/services/noop-view-cache.service.ts b/libs/angular/src/platform/view-cache/noop-view-cache.service.ts similarity index 87% rename from libs/angular/src/platform/services/noop-view-cache.service.ts rename to libs/angular/src/platform/view-cache/noop-view-cache.service.ts index 9953e80b3b0..f83a0fc0b04 100644 --- a/libs/angular/src/platform/services/noop-view-cache.service.ts +++ b/libs/angular/src/platform/view-cache/noop-view-cache.service.ts @@ -1,11 +1,7 @@ import { Injectable, signal, WritableSignal } from "@angular/core"; import type { FormGroup } from "@angular/forms"; -import { - FormCacheOptions, - SignalCacheOptions, - ViewCacheService, -} from "../abstractions/view-cache.service"; +import { FormCacheOptions, SignalCacheOptions, ViewCacheService } from "./view-cache.service"; /** * The functionality of the {@link ViewCacheService} is only needed in the browser extension popup, diff --git a/libs/angular/src/platform/view-cache/view-cache.md b/libs/angular/src/platform/view-cache/view-cache.md new file mode 100644 index 00000000000..c1f80da5800 --- /dev/null +++ b/libs/angular/src/platform/view-cache/view-cache.md @@ -0,0 +1,130 @@ +# Extension Persistence + +By default, when the browser extension popup closes, the user's current view and any data entered +without saving is lost. This introduces friction in several workflows within our client, such as: + +- Performing actions that require email OTP entry, since the user must navigate from the popup to + get to their email inbox +- Entering information to create a new vault item from a browser tab +- And many more + +Previously, we have recommended that users "pop out" the extension into its own window to persist +the extension context, but this introduces additional user actions and may leave the extension open +(and unlocked) for longer than a user intends. + +In order to provide a better user experience, we have introduced two levels of persistence to the +Bitwarden extension client: + +- We persist the route history, allowing us to re-open the last route when the popup re-opens, and +- We offer a service for teams to use to persist component-specific form data or state to survive a + popup close/re-open cycle + +## Persistence lifetime + +Since we are persisting data, it is important that the lifetime of that data be well-understood and +well-constrained. The cache of route history and form data is cleared when any of the following +events occur: + +- The account is locked +- The account is logged out +- Account switching is used to switch the active account +- The extension popup has been closed for 2 minutes + +In addition, cached form data is cleared when a browser extension navigation event occurs (e.g. +switching between tabs in the extension). + +## Types of persistence + +### Route history persistence + +Route history is persisted on the extension automatically, with no specific implementation required +on any component. + +The persistence layer ensures that the popup will open at the same route as was active when it +closed, provided that none of the lifetime expiration events have occurred. + +:::tip Excluding a route + +If a particular route should be excluded from the history and not persisted, add +`doNotSaveUrl: true` to the `data` property on the route. + +::: + +### View data persistence + +Route persistence ensures that the user will land back on the route that they were on when the popup +closed, but it does not persist any state or form data that the user may have modified. In order to +persist that data, the component is responsible for registering that data with the +[`ViewCacheService`](./view-cache.service.ts). +This is done prescriptively to ensure that only necessary data is cached and that it is done with +intention by the component. + +The `ViewCacheService` provides an interface for caching both individual state and `FormGroup`s. + +#### Caching individual data elements + +For individual pieces of state, use the `signal()` method on the `ViewCacheService` to create a +writeable [signal](https://angular.dev/guide/signals) wrapper around the desired state. + +```typescript +const mySignal = this.viewCacheService.signal({ + key: "my-state-key" + initialValue: null +}); +``` + +If a cached value exists, the returned signal will contain the cached data. + +Setting the value should be done through the signal's `set()` method: + +```typescript +const mySignal = this.viewCacheService.signal({ + key: "my-state-key" + initialValue: null +}); +mySignal.set("value") +``` + +:::note Equality comparison + +By default, signals use `Object.is` to determine equality, and `set()` will only trigger updates if +the updated value is not equal to the current signal state. See documentation +[here](https://angular.dev/guide/signals#signal-equality-functions). + +::: + +Putting this together, the most common implementation pattern would be: + +1. **Register the signal** using `ViewCacheService.signal()` on initialization of the component or + service responsible for the state being persisted. +2. **Restore state from the signal:** If cached data exists, the signal will contain that data. The + component or service should use this data to re-create the state from prior to the popup closing. +3. **Set new state** in the cache when it changes. Ensure that any updates to the data are persisted + to the cache with `set()`, so that the cache reflects the latest state. + +#### Caching form data + +For persisting form data, the `ViewCacheService` supplies a `formGroup()` method, which manages the +persistence of any entered form data to the cache and the initialization of the form from the cached +data. You can supply the `FormGroup` in the `control` parameter of the method, and the +`ViewCacheService` will: + +- Initialize the form the a cached value, if it exists +- Save form value to cache when it changes +- Mark the form dirty if the restored value is not `undefined`. + +```typescript +this.loginDetailsForm = this.viewCacheService.formGroup({ + key: "my-form", + control: this.formBuilder.group({ + username: [""], + email: [""], + }), +}); +``` + +## What about other clients? + +The `ViewCacheService` is designed to be injected into shared, client-agnostic components. A +`NoopViewCacheService` is provided and injected for non-extension clients, preserving a single +interface for your components. diff --git a/libs/angular/src/platform/abstractions/view-cache.service.ts b/libs/angular/src/platform/view-cache/view-cache.service.ts similarity index 98% rename from libs/angular/src/platform/abstractions/view-cache.service.ts rename to libs/angular/src/platform/view-cache/view-cache.service.ts index c5ae6c77d1f..498a29aa242 100644 --- a/libs/angular/src/platform/abstractions/view-cache.service.ts +++ b/libs/angular/src/platform/view-cache/view-cache.service.ts @@ -42,6 +42,8 @@ export type FormCacheOptions = BaseCacheOptions< /** * Cache for temporary component state * + * [Read more](./view-cache.md) + * * #### Implementations * - browser extension popup: used to persist UI between popup open and close * - all other clients: noop diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 4e7c558a0f0..470115ae3f0 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -325,13 +325,14 @@ import { import { DeviceTrustToastService as DeviceTrustToastServiceAbstraction } from "../auth/services/device-trust-toast.service.abstraction"; import { DeviceTrustToastService } from "../auth/services/device-trust-toast.service.implementation"; import { FormValidationErrorsService as FormValidationErrorsServiceAbstraction } from "../platform/abstractions/form-validation-errors.service"; -import { ViewCacheService } from "../platform/abstractions/view-cache.service"; import { FormValidationErrorsService } from "../platform/services/form-validation-errors.service"; import { LoggingErrorHandler } from "../platform/services/logging-error-handler"; -import { NoopViewCacheService } from "../platform/services/noop-view-cache.service"; import { AngularThemingService } from "../platform/services/theming/angular-theming.service"; import { AbstractThemingService } from "../platform/services/theming/theming.service.abstraction"; import { safeProvider, SafeProvider } from "../platform/utils/safe-provider"; +import { ViewCacheService } from "../platform/view-cache"; +// eslint-disable-next-line no-restricted-imports -- Needed for DI +import { NoopViewCacheService } from "../platform/view-cache/internal"; import { CLIENT_TYPE, diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-component-email-cache.service.spec.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-component-email-cache.service.spec.ts index f3b904a4ea6..d2d86710b72 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-component-email-cache.service.spec.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-component-email-cache.service.spec.ts @@ -2,7 +2,7 @@ import { TestBed } from "@angular/core/testing"; import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject } from "rxjs"; -import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.spec.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.spec.ts index 1613c0e4af8..36d99ee56ac 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.spec.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.spec.ts @@ -2,7 +2,7 @@ import { TestBed } from "@angular/core/testing"; import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject } from "rxjs"; -import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.ts index e32b6cd1385..d274b8003d7 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.ts @@ -1,7 +1,7 @@ import { inject, Injectable, WritableSignal } from "@angular/core"; import { Jsonify } from "type-fest"; -import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.spec.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.spec.ts index 0993954fde1..5b5d486556b 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.spec.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.spec.ts @@ -2,7 +2,7 @@ import { TestBed } from "@angular/core/testing"; import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject } from "rxjs"; -import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.ts index 61b44aa98dd..2d9fcaa5633 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.ts @@ -1,7 +1,7 @@ import { inject, Injectable, WritableSignal } from "@angular/core"; import { Jsonify } from "type-fest"; -import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; diff --git a/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.spec.ts b/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.spec.ts index 89b78500f1f..ab82b98f614 100644 --- a/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.spec.ts +++ b/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.spec.ts @@ -1,7 +1,7 @@ import { signal } from "@angular/core"; import { TestBed } from "@angular/core/testing"; -import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { LoginViaAuthRequestView } from "@bitwarden/common/auth/models/view/login-via-auth-request.view"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; diff --git a/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.ts b/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.ts index 493fea5c14b..8fbdb925e7c 100644 --- a/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.ts +++ b/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.ts @@ -1,6 +1,6 @@ import { inject, Injectable, WritableSignal } from "@angular/core"; -import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { LoginViaAuthRequestView } from "@bitwarden/common/auth/models/view/login-via-auth-request.view"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; diff --git a/libs/vault/src/cipher-form/cipher-form.stories.ts b/libs/vault/src/cipher-form/cipher-form.stories.ts index 9943f07292d..7090502ef14 100644 --- a/libs/vault/src/cipher-form/cipher-form.stories.ts +++ b/libs/vault/src/cipher-form/cipher-form.stories.ts @@ -13,7 +13,7 @@ import { import { BehaviorSubject } from "rxjs"; import { CollectionView } from "@bitwarden/admin-console/common"; -import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; diff --git a/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts b/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts index 4c61ad5d2d5..da687f33ef9 100644 --- a/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts +++ b/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { ReactiveFormsModule } from "@angular/forms"; import { mock } from "jest-mock-extended"; -import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; diff --git a/libs/vault/src/cipher-form/services/default-cipher-form-cache.service.spec.ts b/libs/vault/src/cipher-form/services/default-cipher-form-cache.service.spec.ts index 6236e2d3dac..0dec46e1b20 100644 --- a/libs/vault/src/cipher-form/services/default-cipher-form-cache.service.spec.ts +++ b/libs/vault/src/cipher-form/services/default-cipher-form-cache.service.spec.ts @@ -1,7 +1,7 @@ import { signal } from "@angular/core"; import { TestBed } from "@angular/core/testing"; -import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; diff --git a/libs/vault/src/cipher-form/services/default-cipher-form-cache.service.ts b/libs/vault/src/cipher-form/services/default-cipher-form-cache.service.ts index 268b2db306b..b4a8138e025 100644 --- a/libs/vault/src/cipher-form/services/default-cipher-form-cache.service.ts +++ b/libs/vault/src/cipher-form/services/default-cipher-form-cache.service.ts @@ -1,6 +1,6 @@ import { inject, Injectable } from "@angular/core"; -import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; From a8fb456329ab1d5eaa32684e43dffc6a8585b829 Mon Sep 17 00:00:00 2001 From: Bryan Cunningham Date: Mon, 12 May 2025 14:38:08 -0400 Subject: [PATCH 24/44] Uif/remove a11y manual flag (#14744) * remove comment left in error * remove runOnly options and manual flag --- .storybook/preview.tsx | 4 ---- .storybook/test-runner.ts | 5 +---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 19a4fc51dc8..a948fce0428 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -28,10 +28,6 @@ const preview: Preview = { parameters: { a11y: { element: "#storybook-root", - manual: true, - options: { - runOnly: ["section508", "wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"], - }, }, controls: { matchers: { diff --git a/.storybook/test-runner.ts b/.storybook/test-runner.ts index 8584a321090..208adf96214 100644 --- a/.storybook/test-runner.ts +++ b/.storybook/test-runner.ts @@ -1,7 +1,4 @@ -import { - // waitForPageReady, - type TestRunnerConfig, -} from "@storybook/test-runner"; +import { type TestRunnerConfig } from "@storybook/test-runner"; import { injectAxe, checkA11y } from "axe-playwright"; const testRunnerConfig: TestRunnerConfig = { From 90d8e822ff09d6c4013cc346972712e902c0f797 Mon Sep 17 00:00:00 2001 From: Vincent Salucci <26154748+vincentsalucci@users.noreply.github.com> Date: Mon, 12 May 2025 14:08:48 -0500 Subject: [PATCH 25/44] chore: remove vault-bulk-management-action flag for the third time, refs PM-13112 (#14747) --- libs/common/src/enums/feature-flag.enum.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 08de8ada25a..aa9a9ab5d6e 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -55,7 +55,6 @@ export enum FeatureFlag { /* Vault */ PM8851_BrowserOnboardingNudge = "pm-8851-browser-onboarding-nudge", PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form", - VaultBulkManagementAction = "vault-bulk-management-action", SecurityTasks = "security-tasks", CipherKeyEncryption = "cipher-key-encryption", PM18520_UpdateDesktopCipherForm = "pm-18520-desktop-cipher-forms", @@ -107,7 +106,6 @@ export const DefaultFeatureFlagValue = { /* Vault */ [FeatureFlag.PM8851_BrowserOnboardingNudge]: FALSE, [FeatureFlag.PM9111ExtensionPersistAddEditForm]: FALSE, - [FeatureFlag.VaultBulkManagementAction]: FALSE, [FeatureFlag.SecurityTasks]: FALSE, [FeatureFlag.CipherKeyEncryption]: FALSE, [FeatureFlag.PM18520_UpdateDesktopCipherForm]: FALSE, From ce8e123c447c8bdbbbc8a6bb0eb1381e837e4fbe Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Mon, 12 May 2025 12:17:15 -0700 Subject: [PATCH 26/44] [PM-21480] - check for privacy granted in isBrowserAutofillSettingOverridden (#14748) * check for privacy granted in isBrowserAutofillSettingOverridden * check for privacy granted in browserAutofillSettingsOverridden --- apps/browser/src/platform/browser/browser-api.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index b27e8ca7c96..4ef72fa0077 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -664,6 +664,10 @@ export class BrowserApi { * Identifies if the browser autofill settings are overridden by the extension. */ static async browserAutofillSettingsOverridden(): Promise { + if (!(await BrowserApi.permissionsGranted(["privacy"]))) { + return false; + } + const checkOverrideStatus = (details: chrome.types.ChromeSettingGetResult) => details.levelOfControl === "controlled_by_this_extension" && !details.value; From b8074a6f730c0179b71f2ccde0b06920d46d57ea Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Mon, 12 May 2025 15:18:02 -0400 Subject: [PATCH 27/44] chore(feature-flag): Remove pm-9112-device-approval-persistence (#14718) --- .../login-via-auth-request.component.ts | 34 +++++------ ...gin-via-auth-request-cache.service.spec.ts | 61 ++++--------------- ...lt-login-via-auth-request-cache.service.ts | 27 -------- libs/common/src/enums/feature-flag.enum.ts | 2 - 4 files changed, 27 insertions(+), 97 deletions(-) diff --git a/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts b/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts index ab5b0c09b32..4c95a1eca3e 100644 --- a/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts +++ b/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts @@ -23,12 +23,10 @@ import { AuthRequest } from "@bitwarden/common/auth/models/request/auth.request" import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { LoginViaAuthRequestView } from "@bitwarden/common/auth/models/view/login-via-auth-request.view"; import { ClientType, HttpStatusCode } from "@bitwarden/common/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -101,7 +99,6 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy { private validationService: ValidationService, private loginSuccessHandlerService: LoginSuccessHandlerService, private loginViaAuthRequestCacheService: LoginViaAuthRequestCacheService, - private configService: ConfigService, ) { this.clientType = this.platformUtilsService.getClientType(); @@ -132,7 +129,6 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy { async ngOnInit(): Promise { // Get the authStatus early because we use it in both flows this.authStatus = await firstValueFrom(this.authService.activeAccountStatus$); - await this.loginViaAuthRequestCacheService.init(); const userHasAuthenticatedViaSSO = this.authStatus === AuthenticationStatus.Locked; @@ -410,24 +406,22 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy { const authRequestResponse: AuthRequestResponse = await this.authRequestApiService.postAuthRequest(authRequest); - if (await this.configService.getFeatureFlag(FeatureFlag.PM9112_DeviceApprovalPersistence)) { - if (!this.authRequestKeyPair.privateKey) { - this.logService.error("No private key when trying to cache the login view."); - return; - } - - if (!this.accessCode) { - this.logService.error("No access code when trying to cache the login view."); - return; - } - - this.loginViaAuthRequestCacheService.cacheLoginView( - authRequestResponse.id, - this.authRequestKeyPair.privateKey, - this.accessCode, - ); + if (!this.authRequestKeyPair.privateKey) { + this.logService.error("No private key when trying to cache the login view."); + return; } + if (!this.accessCode) { + this.logService.error("No access code when trying to cache the login view."); + return; + } + + this.loginViaAuthRequestCacheService.cacheLoginView( + authRequestResponse.id, + this.authRequestKeyPair.privateKey, + this.accessCode, + ); + if (authRequestResponse.id) { await this.anonymousHubService.createHubConnection(authRequestResponse.id); } diff --git a/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.spec.ts b/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.spec.ts index ab82b98f614..67ac605e611 100644 --- a/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.spec.ts +++ b/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.spec.ts @@ -3,7 +3,6 @@ import { TestBed } from "@angular/core/testing"; import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { LoginViaAuthRequestView } from "@bitwarden/common/auth/models/view/login-via-auth-request.view"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { LoginViaAuthRequestCacheService } from "./default-login-via-auth-request-cache.service"; @@ -14,74 +13,40 @@ describe("LoginViaAuthRequestCache", () => { const cacheSignal = signal(null); const getCacheSignal = jest.fn().mockReturnValue(cacheSignal); - const getFeatureFlag = jest.fn().mockResolvedValue(false); const cacheSetMock = jest.spyOn(cacheSignal, "set"); beforeEach(() => { getCacheSignal.mockClear(); - getFeatureFlag.mockClear(); cacheSetMock.mockClear(); testBed = TestBed.configureTestingModule({ providers: [ { provide: ViewCacheService, useValue: { signal: getCacheSignal } }, - { provide: ConfigService, useValue: { getFeatureFlag } }, LoginViaAuthRequestCacheService, ], }); }); - describe("feature enabled", () => { - beforeEach(() => { - getFeatureFlag.mockResolvedValue(true); - }); + it("`getCachedLoginViaAuthRequestView` returns the cached data", async () => { + cacheSignal.set({ ...buildMockState() }); + service = testBed.inject(LoginViaAuthRequestCacheService); - it("`getCachedLoginViaAuthRequestView` returns the cached data", async () => { - cacheSignal.set({ ...buildMockState() }); - service = testBed.inject(LoginViaAuthRequestCacheService); - await service.init(); - - expect(service.getCachedLoginViaAuthRequestView()).toEqual({ - ...buildMockState(), - }); - }); - - it("updates the signal value", async () => { - service = testBed.inject(LoginViaAuthRequestCacheService); - await service.init(); - - const parameters = buildAuthenticMockAuthView(); - - service.cacheLoginView(parameters.id, parameters.privateKey, parameters.accessCode); - - expect(cacheSignal.set).toHaveBeenCalledWith({ - id: parameters.id, - privateKey: Utils.fromBufferToB64(parameters.privateKey), - accessCode: parameters.accessCode, - }); + expect(service.getCachedLoginViaAuthRequestView()).toEqual({ + ...buildMockState(), }); }); - describe("feature disabled", () => { - beforeEach(async () => { - cacheSignal.set({ ...buildMockState() } as LoginViaAuthRequestView); - getFeatureFlag.mockResolvedValue(false); - cacheSetMock.mockClear(); + it("updates the signal value", async () => { + service = testBed.inject(LoginViaAuthRequestCacheService); - service = testBed.inject(LoginViaAuthRequestCacheService); - await service.init(); - }); + const parameters = buildAuthenticMockAuthView(); - it("`getCachedCipherView` returns null", () => { - expect(service.getCachedLoginViaAuthRequestView()).toBeNull(); - }); + service.cacheLoginView(parameters.id, parameters.privateKey, parameters.accessCode); - it("does not update the signal value", () => { - const params = buildAuthenticMockAuthView(); - - service.cacheLoginView(params.id, params.privateKey, params.accessCode); - - expect(cacheSignal.set).not.toHaveBeenCalled(); + expect(cacheSignal.set).toHaveBeenCalledWith({ + id: parameters.id, + privateKey: Utils.fromBufferToB64(parameters.privateKey), + accessCode: parameters.accessCode, }); }); diff --git a/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.ts b/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.ts index 8fbdb925e7c..80dbafd3159 100644 --- a/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.ts +++ b/libs/auth/src/common/services/auth-request/default-login-via-auth-request-cache.service.ts @@ -2,8 +2,6 @@ import { inject, Injectable, WritableSignal } from "@angular/core"; import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { LoginViaAuthRequestView } from "@bitwarden/common/auth/models/view/login-via-auth-request.view"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; const LOGIN_VIA_AUTH_CACHE_KEY = "login-via-auth-request-form-cache"; @@ -17,10 +15,6 @@ const LOGIN_VIA_AUTH_CACHE_KEY = "login-via-auth-request-form-cache"; @Injectable() export class LoginViaAuthRequestCacheService { private viewCacheService: ViewCacheService = inject(ViewCacheService); - private configService: ConfigService = inject(ConfigService); - - /** True when the `PM9112_DeviceApproval` flag is enabled */ - private featureEnabled: boolean = false; private defaultLoginViaAuthRequestCache: WritableSignal = this.viewCacheService.signal({ @@ -31,23 +25,10 @@ export class LoginViaAuthRequestCacheService { constructor() {} - /** - * Must be called once before interacting with the cached data, otherwise methods will be noop. - */ - async init() { - this.featureEnabled = await this.configService.getFeatureFlag( - FeatureFlag.PM9112_DeviceApprovalPersistence, - ); - } - /** * Update the cache with the new LoginView. */ cacheLoginView(id: string, privateKey: Uint8Array, accessCode: string): void { - if (!this.featureEnabled) { - return; - } - // When the keys get stored they should be converted to a B64 string to ensure // data can be properly formed when json-ified. If not done, they are not stored properly and // will not be parsable by the cryptography library after coming out of storage. @@ -59,10 +40,6 @@ export class LoginViaAuthRequestCacheService { } clearCacheLoginView(): void { - if (!this.featureEnabled) { - return; - } - this.defaultLoginViaAuthRequestCache.set(null); } @@ -70,10 +47,6 @@ export class LoginViaAuthRequestCacheService { * Returns the cached LoginViaAuthRequestView when available. */ getCachedLoginViaAuthRequestView(): LoginViaAuthRequestView | null { - if (!this.featureEnabled) { - return null; - } - return this.defaultLoginViaAuthRequestCache(); } } diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index aa9a9ab5d6e..a7679a6248a 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -16,7 +16,6 @@ export enum FeatureFlag { SeparateCustomRolePermissions = "pm-19917-separate-custom-role-permissions", /* Auth */ - PM9112_DeviceApprovalPersistence = "pm-9112-device-approval-persistence", PM9115_TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence", /* Autofill */ @@ -112,7 +111,6 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.EndUserNotifications]: FALSE, /* Auth */ - [FeatureFlag.PM9112_DeviceApprovalPersistence]: FALSE, [FeatureFlag.PM9115_TwoFactorExtensionDataPersistence]: FALSE, /* Billing */ From 7d01c42e37e07d6642a2fbda5eb27cc12fdf4f85 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 15:42:21 -0400 Subject: [PATCH 28/44] [deps] Vault: Update multer to v1.4.5-lts.2 (#14469) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: SmithThe4th --- apps/cli/package.json | 2 +- package-lock.json | 10 +++++----- package.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 79d4786a23c..0a7e66d7818 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -81,7 +81,7 @@ "koa-json": "2.0.2", "lowdb": "1.0.0", "lunr": "2.3.9", - "multer": "1.4.5-lts.1", + "multer": "1.4.5-lts.2", "node-fetch": "2.6.12", "node-forge": "1.3.1", "open": "8.4.2", diff --git a/package-lock.json b/package-lock.json index 81c9384fbbb..44bebc9cbd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,7 +54,7 @@ "lit": "3.2.1", "lowdb": "1.0.0", "lunr": "2.3.9", - "multer": "1.4.5-lts.1", + "multer": "1.4.5-lts.2", "ngx-toastr": "19.0.0", "node-fetch": "2.6.12", "node-forge": "1.3.1", @@ -217,7 +217,7 @@ "koa-json": "2.0.2", "lowdb": "1.0.0", "lunr": "2.3.9", - "multer": "1.4.5-lts.1", + "multer": "1.4.5-lts.2", "node-fetch": "2.6.12", "node-forge": "1.3.1", "open": "8.4.2", @@ -28598,9 +28598,9 @@ } }, "node_modules/multer": { - "version": "1.4.5-lts.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", - "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "version": "1.4.5-lts.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", + "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", "license": "MIT", "dependencies": { "append-field": "^1.0.0", diff --git a/package.json b/package.json index 8ba1c95df6a..47cede513ed 100644 --- a/package.json +++ b/package.json @@ -190,7 +190,7 @@ "lit": "3.2.1", "lowdb": "1.0.0", "lunr": "2.3.9", - "multer": "1.4.5-lts.1", + "multer": "1.4.5-lts.2", "ngx-toastr": "19.0.0", "node-fetch": "2.6.12", "node-forge": "1.3.1", From 9af7963609a08a5e00dd209e5614022c0d58350a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 14:58:48 -0500 Subject: [PATCH 29/44] [deps] Vault: Update @koa/multer to v3.1.0 (#14495) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> --- apps/cli/package.json | 2 +- package-lock.json | 68 ++++++++++++++++++++----------------------- package.json | 2 +- 3 files changed, 33 insertions(+), 39 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 0a7e66d7818..82faa7d40e6 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -64,7 +64,7 @@ ] }, "dependencies": { - "@koa/multer": "3.0.2", + "@koa/multer": "3.1.0", "@koa/router": "13.1.0", "argon2": "0.41.1", "big-integer": "1.6.52", diff --git a/package-lock.json b/package-lock.json index 44bebc9cbd4..8466a4ba4c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "@bitwarden/sdk-internal": "0.2.0-main.159", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", - "@koa/multer": "3.0.2", + "@koa/multer": "3.1.0", "@koa/router": "13.1.0", "@microsoft/signalr": "8.0.7", "@microsoft/signalr-protocol-msgpack": "8.0.7", @@ -200,7 +200,7 @@ "version": "2025.4.0", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { - "@koa/multer": "3.0.2", + "@koa/multer": "3.1.0", "@koa/router": "13.1.0", "argon2": "0.41.1", "big-integer": "1.6.52", @@ -389,6 +389,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -2745,6 +2746,7 @@ "version": "7.26.8", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -2754,6 +2756,7 @@ "version": "7.24.9", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", + "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", @@ -2784,12 +2787,14 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, "license": "MIT" }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -2799,6 +2804,7 @@ "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", + "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.26.10", @@ -2828,6 +2834,7 @@ "version": "7.26.5", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.26.5", @@ -2844,6 +2851,7 @@ "version": "4.24.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, "funding": [ { "type": "opencollective", @@ -2876,6 +2884,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3015,6 +3024,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", @@ -3045,6 +3055,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -3148,6 +3159,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -3172,6 +3184,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.1", @@ -3280,23 +3293,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", @@ -3382,6 +3378,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" @@ -4010,6 +4007,7 @@ "version": "7.26.3", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.26.0", @@ -7855,15 +7853,12 @@ } }, "node_modules/@koa/multer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@koa/multer/-/multer-3.0.2.tgz", - "integrity": "sha512-Q6WfPpE06mJWyZD1fzxM6zWywaoo+zocAn2YA9QYz4RsecoASr1h/kSzG0c5seDpFVKCMZM9raEfuM7XfqbRLw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@koa/multer/-/multer-3.1.0.tgz", + "integrity": "sha512-ETf4OLpOew9XE9lyU+5HIqk3TCmdGAw9pUXgxzrlYip+PkxLGoU4meiVTxiW4B6lxdBNijb3DFQ7M2woLcDL1g==", "license": "MIT", - "dependencies": { - "fix-esm": "1.0.1" - }, "engines": { - "node": ">= 8" + "node": ">= 14" }, "peerDependencies": { "multer": "*" @@ -15362,6 +15357,7 @@ "version": "4.23.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "dev": true, "funding": [ { "type": "opencollective", @@ -15951,6 +15947,7 @@ "version": "1.0.30001717", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz", "integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==", + "dev": true, "funding": [ { "type": "opencollective", @@ -18482,6 +18479,7 @@ "version": "1.5.151", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.151.tgz", "integrity": "sha512-Rl6uugut2l9sLojjS4H4SAr3A4IgACMLgpuEMPYCVcKydzfyPrn5absNRju38IhQOf/NwjJY8OGWjlteqYeBCA==", + "dev": true, "license": "ISC" }, "node_modules/electron-updater": { @@ -18965,6 +18963,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -20387,17 +20386,6 @@ "micromatch": "^4.0.2" } }, - "node_modules/fix-esm": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fix-esm/-/fix-esm-1.0.1.tgz", - "integrity": "sha512-EZtb7wPXZS54GaGxaWxMlhd1DUDCnAg5srlYdu/1ZVeW+7wwR3Tp59nu52dXByFs3MBRq+SByx1wDOJpRvLEXw==", - "license": "WTFPL OR CC0-1.0", - "dependencies": { - "@babel/core": "^7.14.6", - "@babel/plugin-proposal-export-namespace-from": "^7.14.5", - "@babel/plugin-transform-modules-commonjs": "^7.14.5" - } - }, "node_modules/flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", @@ -21084,6 +21072,7 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -25452,6 +25441,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -27032,6 +27022,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" @@ -29278,6 +29269,7 @@ "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, "license": "MIT" }, "node_modules/nopt": { @@ -37088,6 +37080,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, "funding": [ { "type": "opencollective", @@ -38970,6 +38963,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, "license": "ISC" }, "node_modules/yaml": { diff --git a/package.json b/package.json index 47cede513ed..2993707313f 100644 --- a/package.json +++ b/package.json @@ -163,7 +163,7 @@ "@bitwarden/sdk-internal": "0.2.0-main.159", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", - "@koa/multer": "3.0.2", + "@koa/multer": "3.1.0", "@koa/router": "13.1.0", "@microsoft/signalr": "8.0.7", "@microsoft/signalr-protocol-msgpack": "8.0.7", From 63b224f3dd08f692c14e18109fba4463062b5c63 Mon Sep 17 00:00:00 2001 From: Daniel Riera Date: Mon, 12 May 2025 17:03:09 -0400 Subject: [PATCH 30/44] PM-21454 Firefox - "null" text is showing in Add/Update Confirmation notification (#14745) * PM-21454 * Update apps/browser/src/autofill/content/components/lit-stories/mock-data.ts Co-authored-by: Jonathan Prusik * Update apps/browser/src/autofill/content/components/lit-stories/mock-data.ts Co-authored-by: Jonathan Prusik * Update apps/browser/src/autofill/content/components/notification/confirmation/container.ts Co-authored-by: Jonathan Prusik * run lint --------- Co-authored-by: Jonathan Prusik --- apps/browser/src/_locales/en/messages.json | 19 ++---- .../components/lit-stories/mock-data.ts | 4 +- .../notification/confirmation/container.ts | 9 ++- .../notification/confirmation/message.ts | 63 ++++++++++++------- apps/browser/src/autofill/notification/bar.ts | 6 +- 5 files changed, 57 insertions(+), 44 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 57a0e78fd1b..361093b12ef 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1088,22 +1088,13 @@ } } }, - "loginSaveConfirmation": { - "message": "$ITEMNAME$ saved to Bitwarden.", - "placeholders": { - "itemName": { - "content": "$1" - } - }, + "notificationLoginSaveConfirmation": { + "message": "saved to Bitwarden.", + "description": "Shown to user after item is saved." }, - "loginUpdatedConfirmation": { - "message": "$ITEMNAME$ updated in Bitwarden.", - "placeholders": { - "itemName": { - "content": "$1" - } - }, + "notificationLoginUpdatedConfirmation": { + "message": "updated in Bitwarden.", "description": "Shown to user after item is updated." }, "saveAsNewLoginAction": { diff --git a/apps/browser/src/autofill/content/components/lit-stories/mock-data.ts b/apps/browser/src/autofill/content/components/lit-stories/mock-data.ts index b8e35813c6c..d134db3ca6f 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/mock-data.ts +++ b/apps/browser/src/autofill/content/components/lit-stories/mock-data.ts @@ -107,9 +107,9 @@ export const mockI18n = { collection: "Collection", folder: "Folder", loginSaveSuccess: "Login saved", - loginSaveConfirmation: "$ITEMNAME$ saved to Bitwarden.", + notificationLoginSaveConfirmation: "saved to Bitwarden.", loginUpdateSuccess: "Login updated", - loginUpdatedConfirmation: "$ITEMNAME$ updated in Bitwarden.", + notificationLoginUpdatedConfirmation: "updated in Bitwarden.", loginUpdateTaskSuccess: "Great job! You took the steps to make you and $ORGANIZATION$ more secure.", loginUpdateTaskSuccessAdditional: diff --git a/apps/browser/src/autofill/content/components/notification/confirmation/container.ts b/apps/browser/src/autofill/content/components/notification/confirmation/container.ts index 7ad6b4542ec..d4d66c7a7be 100644 --- a/apps/browser/src/autofill/content/components/notification/confirmation/container.ts +++ b/apps/browser/src/autofill/content/components/notification/confirmation/container.ts @@ -113,9 +113,14 @@ function getConfirmationMessage(i18n: I18n, type?: NotificationType, error?: str if (error) { return i18n.saveFailureDetails; } + + /* @TODO This partial string return and later concatenation with the cipher name is needed + * to handle cipher name overflow cases, but is risky for i18n concerns. Fix concatenation + * with cipher name overflow when a tag replacement solution is available. + */ return type === NotificationTypes.Add - ? i18n.loginSaveConfirmation - : i18n.loginUpdatedConfirmation; + ? i18n.notificationLoginSaveConfirmation + : i18n.notificationLoginUpdatedConfirmation; } function getHeaderMessage(i18n: I18n, type?: NotificationType, error?: string) { diff --git a/apps/browser/src/autofill/content/components/notification/confirmation/message.ts b/apps/browser/src/autofill/content/components/notification/confirmation/message.ts index 527119aed15..2bf8caecfff 100644 --- a/apps/browser/src/autofill/content/components/notification/confirmation/message.ts +++ b/apps/browser/src/autofill/content/components/notification/confirmation/message.ts @@ -28,28 +28,31 @@ export function NotificationConfirmationMessage({