diff --git a/.gitlab-ci/jobs/build.yml b/.gitlab-ci/jobs/build.yml index b6fbaf10..110ab262 100644 --- a/.gitlab-ci/jobs/build.yml +++ b/.gitlab-ci/jobs/build.yml @@ -1,6 +1,6 @@ build: stage: build - image: node:16 + image: node:18 extends: .rules artifacts: when: always @@ -22,7 +22,7 @@ build: build_mv3: stage: build - image: node:16 + image: node:18 extends: .rules artifacts: when: always diff --git a/.gitlab-ci/jobs/test.yml b/.gitlab-ci/jobs/test.yml index 90cb974c..7db1e011 100644 --- a/.gitlab-ci/jobs/test.yml +++ b/.gitlab-ci/jobs/test.yml @@ -1,6 +1,6 @@ tester: stage: test - image: node:16 + image: node:18 coverage: /Lines\s* [:] ([\d\.]+)%/ extends: .rules script: @@ -17,7 +17,7 @@ tester: linter: stage: test - image: node:16 + image: node:18 extends: .rules script: - npm ci @@ -26,7 +26,7 @@ linter: audit: allow_failure: true stage: test - image: node:16 + image: node:18 extends: .rules script: - npm audit diff --git a/CHANGELOG.md b/CHANGELOG.md index 20f8fbfb..19c94337 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,63 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [4.4.0-rc.0] - 2023-11-03 +## Browser extension +### Added +- PB-25204 As a signed-in user I can create a standalone TOTP +- PB-25206 As a signed-in user I can add a TOTP to an existing password resource +- PB-25210 As a signed-in user I can edit a standalone TOTP +- PB-25224 As a signed-in user I can copy a TOTP +- PB-26088 As a signed-in user I can see standalone TOTP in the quickaccess +- PB-27600 As an administrator I want to suspend or unsuspend a user +- PB-27601 As a signed-in user I should see who is suspended in the ui +- PB-27773 As an administrator I can deny access to the mobile setup screen with RBAC +- PB-27898 As an administrator I should have the possibility to deny TOTP copy and preview actions with RBAC +- PB-27949 As a signed-in user I can see password with totp in the quickaccess +- PB-27950 As a user I can use generic OAuth2 as single sign on provider +- [FEATURE INACTIVE] PB-28263 As a user I can see the resource expiry status +- [FEATURE INACTIVE] PB-28265 As a user I can reset resource expiry date +- [FEATURE INACTIVE] PB-28266 As an administrator I can enable the password expiry feature +- [FEATURE INACTIVE] PB-28267 As an administrator I can set the email notifications of the password expiry feature + +### Improved +- PB-19244 As a user with encrypted description resource type present when creating a resource using quickaccess the description should be encrypted by default +- PB-25560 As an administrator on the admin settings pages I can see the source of information +- PB-26002 As a user downloading my recovery kit I want to be warned about the critical character of this asset +- PB-26086 As an administrator generating an account recovery key for my organization I want to confirm the passphrase +- PB-26094 As an administrator having a passbolt trespassing the user limits I should see a better message +- PB-27668 As a user I'd like to know what the numbers by the heart mean +- PB-27922 As a user entering my passphrase I should see the entropy progressing +- PB-28183 As administrator I want to see warnings while synchronising the organisation users directory +- PB-28378 MFA screen should be display depending on the application + +### Fixed +- PB-21625 As a user I shouldn't see apostrophe replaced by special characters +- PB-25279 As a user I should see in form call to action icon be well positioned +- PB-26000 As a user updating only a resource metadata I should not update the resource secret on the API +- PB-27784 As an administrator I should not see the account recovery enrollment twice +- PB-27794 Fix unsupported TOTP while decrypting TOTP on webapp +- PB-27894 As a user I should not see my username overpass the card in the login form +- PB-27947 Fix in-form menu generate password should not override all password fields but only new password fields +- PB-27954 Fix message after successful transfer to mobile +- PB-28170 Fix SMTP host from Sendgrid +- PB-28310 As a signed-in user I should not select or unselect a resource on TOTP click +- PB-28293 As a signed-in user I should be redirected when I click on the resource url in the information panel and contextual menu + +### Maintenance +- PB-26121 Improve Styleguide coverage of password policies +- PB-27786 As a user I should not see my passphrase part of the breach if the field is empty +- PB-27945 Update web-ext lib to v7.8.0 +- PB-27965 Upgrade node to v18 +- PB-28148 Migrate development watcher to package.json scripts +- PB-28275 Upgrade @babel/traverse on styleguide as it has a critical security issue +- [FEATURE INACTIVE] PB-27605 As a signed-in user I can set up Yubikey as two-factor authentication on the client (previously done on the API served application) +- [FEATURE INACTIVE] PB-27606 As a signed-in user I can set up TOTP as two-factor authentication on the client (previously done on the API served application) +- [FEATURE INACTIVE] PB-27608 As a user I can sign in with TOTP and Yubikey as 2FA on the client (previously done on the API served application) + +### Security +- PB-25688 As a desktop app user I should sign the exported account kit with my private key + ## [4.3.1] - 2023-09-28 ### Fixed - PB-27860 As a signed-in user I should be able to autofill from the quickaccess @@ -1334,6 +1391,7 @@ self registration settings option in the left-side bar - LU: Logged in user [Unreleased]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.2.0...HEAD +[4.4.0-rc.0]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.3.1...v.4.4.0-rc.0 [4.3.1]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.3.0...v.4.3.1 [4.3.0]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.2.0...v.4.3.0 [4.2.0]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.1.2...v.4.2.0 diff --git a/Gruntfile.js b/Gruntfile.js index 6526c5bc..36599c98 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -44,7 +44,6 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-shell'); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-copy'); - grunt.loadNpmTasks('grunt-contrib-watch'); grunt.registerTask('default', ['bundle']); grunt.registerTask('pre-dist', ['copy:styleguide']); @@ -450,88 +449,6 @@ module.exports = function (grunt) { "echo '\nZip and Crx files generated in " + path.dist_chrome_mv3 + "'" ].join(' && ') } - }, - /** - * Watch task run predefined tasks whenever watched file patterns are added, changed or deleted - * see. https://github.com/gruntjs/grunt-contrib-watch - */ - watch: { - background_page: { - files: [path.src_background_page + '**/*.js', 'node_modules/passbolt-styleguide/src/shared/**/*.js'], - tasks: ['shell:build_background_page_debug'], - options: { spawn: false } - }, - service_worker: { - files: [`${path.src_background_page}**/*.js`, `${path.src_chrome_mv3}**/*.js`], - tasks: ['shell:build_service_worker_debug', 'copy:service_worker'], - options: { spawn: false } - }, - content_script_app: { - files: [ - path.src_content_scripts + 'js/app/AccountRecovery.js', - path.src_content_scripts + 'js/app/App.js', - path.src_content_scripts + 'js/app/Login.js', - path.src_content_scripts + 'js/app/Recover.js', - path.src_content_scripts + 'js/app/Setup.js' - ], - tasks: ['shell:build_content_script_app'], - options: { spawn: false } - }, - content_script_browser_integration: { - files: [path.src_content_scripts + 'js/app/BrowserIntegration.js'], - tasks: ['shell:build_content_script_browser_integration'], - options: { spawn: false } - }, - content_script_public_website: { - files: [path.src_content_scripts + 'js/app/PublicWebsiteSignIn.js'], - tasks: ['shell:build_content_script_public_website'], - options: { spawn: false } - }, - web_accessible_resources: { - files: [ - path.src_web_accessible_resources + 'js/themes/*.js', - path.src_web_accessible_resources + '*.html' - ], - tasks: ['copy:web_accessible_resources'], - options: { spawn: false } - }, - web_accessible_resources_app: { - files: [ - path.src_web_accessible_resources + 'js/app/AccountRecovery.js', - path.src_web_accessible_resources + 'js/app/App.js', - path.src_web_accessible_resources + 'js/app/Download.js', - path.src_web_accessible_resources + 'js/app/Login.js', - path.src_web_accessible_resources + 'js/app/QuickAccess.js', - path.src_web_accessible_resources + 'js/app/Recover.js', - path.src_web_accessible_resources + 'js/app/Setup.js', - path.src_web_accessible_resources + 'js/app/Setup.js' - ], - tasks: ['shell:build_web_accessible_resources_app'], - options: { spawn: false } - }, - web_accessible_resources_browser_integration: { - files: [ - path.src_web_accessible_resources + 'js/app/InFormCallToAction.js', - path.src_web_accessible_resources + 'js/app/InFormMenu.js' - ], - tasks: ['shell:build_web_accessible_resources_browser_integration'], - options: { spawn: false } - }, - manifest_firefox: { - files: [path.src_firefox + 'manifest.json'], - tasks: ['copy:manifest_firefox'], - options: { spawn: false } - }, - manifest_chrome: { - files: [path.src_chrome + 'manifest.json'], - tasks: ['copy:manifest_chrome'], - options: { spawn: false } - }, - manifest_chrome_mv3: { - files: [path.src_chrome_mv3 + 'manifest.json'], - tasks: ['copy:manifest_chrome_mv3'], - options: { spawn: false } - } } }); }; diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index eb412b74..a2c1c19e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,9 +1,70 @@ -Release song: https://www.youtube.com/watch?v=sc5iTNVEOAg +Release song: https://www.youtube.com/watch?v=6Ejga4kJUts -This is a small maintenance release of the browser extension only. It fixes a bug that prevented users from using the auto-fill feature from the quickaccess. +Version 4.4 (Release Candidate) of Passbolt is now available, packed full of improvements and new functionalities. -Thank you for choosing passbolt and for your continued support. +With this release, users are able to manage TOTPs directly from the browser, providing an extended TOTP experience across all their devices. They can now be created, deleted, organised and shared with others just like any other resource type. + +Another highlight of this release, administrators now have the ability to suspend/unsuspend users. This new feature will offer administrators with more control over access management of their instance. By example, they will be able to prevent access to the passbolt instance for users in temporary leave, therefore enforce company policies. + +Admins of the PRO have an additional option for SSO: a generic OAuth 2.0 provider is now available, expanding your authentication options and providing even more versatility. + +And that's not all – a number of fixes and enhancements have been implemented to improve user experience. Among them, notification emails are now aggregated in certain cases, including limiting emails when a user imports a large amount of passwords. + +Upgrade to version 4.4 to take advantage of these improvements. Thank you for using and supporting passbolt! + +## [4.4.0-rc.0] - 2023-11-03 +## Browser extension +### Added +- PB-25204 As a signed-in user I can create a standalone TOTP +- PB-25206 As a signed-in user I can add a TOTP to an existing password resource +- PB-25210 As a signed-in user I can edit a standalone TOTP +- PB-25224 As a signed-in user I can copy a TOTP +- PB-26088 As a signed-in user I can see standalone TOTP in the quickaccess +- PB-27600 As an administrator I want to suspend or unsuspend a user +- PB-27601 As a sign in user I should see who is suspended in the ui +- PB-27773 As an administrator I can deny access to the mobile setup screen with RBAC +- PB-27898 As an administrator I should have the possibility to deny TOTP copy and preview actions with RBAC +- PB-27949 As a signed-in user I can see password with totp in the quickaccess +- PB-27950 As a user I can use generic OAuth2 as single sign on provider +- [FEATURE INACTIVE] PB-28263 As a user I can see the resource expiry status +- [FEATURE INACTIVE] PB-28265 As a user I can reset resource expiry date +- [FEATURE INACTIVE] PB-28266 As an administrator I can enable the password expiry feature +- [FEATURE INACTIVE] PB-28267 As an administrator I can set the email notifications of the password expiry feature + +### Improved +- PB-19244 As a user with encrypted description resource type present when creating a resource using quickaccess the description should be encrypted by default +- PB-25560 As an administrator on the admin settings pages I can see the source of information +- PB-26002 As a user downloading my recovery kit I want to be warned about the critical character of this asset +- PB-26086 As an administrator generating an account recovery key for my organization I want to confirm the passphrase +- PB-26094 As an administrator having a passbolt trespassing the user limits I should see a better message +- PB-27668 As a user I'd like to know what the numbers by the heart mean +- PB-27922 As a user entering my passphrase I should see the entropy progressing +- PB-28183 As administrator I want to see warnings while synchronising the organisation users directory +- PB-28378 MFA screen should be display depending on the application -## [4.3.1] - 2023-09-28 ### Fixed -- PB-27860 As a signed-in user I should be able to autofill from the quickaccess \ No newline at end of file +- PB-21625 As a user I shouldn't see apostrophe replaced by special characters +- PB-25279 As a user I should see in form call to action icon be well positioned +- PB-26000 As a user updating only a resource metadata I should not update the resource secret on the API +- PB-27784 As an administrator I should not see the account recovery enrollment twice +- PB-27794 Fix unsupported TOTP while decrypting TOTP on webapp +- PB-27894 As a user I should not see my username overpass the card in the login form +- PB-27947 Fix in-form menu generate password should not override all password fields but only new password fields +- PB-27954 Fix message after successful transfer to mobile +- PB-28170 Fix SMTP host from Sendgrid +- PB-28310 As a signed-in user I should not select or unselect a resource on TOTP click +- PB-28293 As a signed-in user I should be redirected when I click on the resource url in the information panel and contextual menu + +### Maintenance +- PB-26121 Improve Styleguide coverage of password policies +- PB-27786 As a user I should not see my passphrase part of the breach if the field is empty +- PB-27945 Update web-ext lib to v7.8.0 +- PB-27965 Upgrade node to v18 +- PB-28148 Migrate development watcher to package.json scripts +- PB-28275 Upgrade @babel/traverse on styleguide as it has a critical security issue +- [FEATURE INACTIVE] PB-27605 As a signed-in user I can set up Yubikey as two-factor authentication on the client (previously done on the API served application) +- [FEATURE INACTIVE] PB-27606 As a signed-in user I can set up TOTP as two-factor authentication on the client (previously done on the API served application) +- [FEATURE INACTIVE] PB-27608 As a user I can sign in with TOTP and Yubikey as 2FA on the client (previously done on the API served application) + +### Security +- PB-25688 As a desktop app user I should sign the exported account kit with my private key diff --git a/package-lock.json b/package-lock.json index a6744b6c..45eabb36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "passbolt-browser-extension", - "version": "4.3.1", + "version": "4.4.0-rc.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "passbolt-browser-extension", - "version": "4.3.1", + "version": "4.4.0-rc.0", "license": "AGPL-3.0", "dependencies": { "await-lock": "^2.1.0", @@ -19,7 +19,7 @@ "locutus": "~2.0.9", "openpgp": "5.2.1", "papaparse": "^5.2.0", - "passbolt-styleguide": "^4.3.0", + "passbolt-styleguide": "^4.4.1", "react": "17.0.2", "react-dom": "17.0.2", "secrets-passbolt": "github:passbolt/secrets.js#v2.0.1", @@ -28,7 +28,7 @@ "xregexp": "~5.1.0" }, "devDependencies": { - "@babel/core": "^7.22.9", + "@babel/core": "^7.23.2", "@babel/eslint-parser": "^7.22.9", "@babel/plugin-transform-runtime": "^7.22.9", "@babel/preset-env": "^7.22.9", @@ -45,7 +45,6 @@ "grunt-browserify": "^6.0.0", "grunt-contrib-clean": "^2.0.0", "grunt-contrib-copy": "^1.0.0", - "grunt-contrib-watch": "^1.1.0", "grunt-shell": "^4.0.0", "i18next-parser": "^6.5.0", "jest": "^29.6.2", @@ -57,13 +56,13 @@ "jest-webextension-mock": "^3.8.9", "text-encoding-utf-8": "^1.0.2", "uuid": "^8.3.2", - "web-ext": "7.6.2", + "web-ext": "^7.8.0", "webpack": "^5.76.3", "webpack-cli": "^4.9.1" }, "engines": { - "node": ">=16.14.0", - "npm": ">=8.3.1" + "node": ">=18.18.0", + "npm": ">=9.8.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -89,11 +88,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" @@ -109,25 +109,25 @@ } }, "node_modules/@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", + "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "^6.3.1" }, "engines": { @@ -138,6 +138,12 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "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 + }, "node_modules/@babel/eslint-parser": { "version": "7.22.9", "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.9.tgz", @@ -157,12 +163,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", - "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -196,22 +202,19 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", - "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", "browserslist": "^4.21.9", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-create-class-features-plugin": { @@ -271,22 +274,22 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -317,28 +320,28 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -448,17 +451,17 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "dev": true, "engines": { "node": ">=6.9.0" @@ -479,26 +482,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", - "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -506,9 +509,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", - "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1871,33 +1874,33 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", - "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/types": "^7.22.5", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1906,13 +1909,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2019,9 +2022,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", - "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -2048,9 +2051,9 @@ "dev": true }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.22.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", + "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -2087,9 +2090,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", - "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2146,6 +2149,50 @@ "react": "*" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/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, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2870,9 +2917,9 @@ "dev": true }, "node_modules/@mdn/browser-compat-data": { - "version": "5.2.42", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.2.42.tgz", - "integrity": "sha512-CD/2ai1W45cDN/zN2AcYduDavU+nq9aStyQizi4MHxnwkRvS/H24WIjgc1qD8CISoqXa8AAIe+A+zpWxwV7a2Q==", + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.3.14.tgz", + "integrity": "sha512-Y9XQrphVcE6u9xMm+gIqN86opbU/5s2W1pdPyKRyFV5B7+2jWM2gLI5JpfhZncaoDKvhy6FYwK04aCz5UM/bTQ==", "dev": true }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { @@ -2919,6 +2966,16 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -3742,58 +3799,83 @@ } }, "node_modules/addons-linter": { - "version": "5.32.0", - "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-5.32.0.tgz", - "integrity": "sha512-Lf6oOyw8X9z5BMd9xhQwSbPlN2PUlzDLnYLAVT5lkrgXEx0fO9hRk4JRxWZ8+rFGz+mCIA2TTClZF2f+MKgJQA==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-6.13.0.tgz", + "integrity": "sha512-vYgDXl8aLmN1zU4HmsQdG6tUFByg499mHnTEMWDUbSkoYDq3koTne08EsqU6sD+o814u8FxclQP7580L0g/tPQ==", "dev": true, "dependencies": { "@fluent/syntax": "0.19.0", - "@mdn/browser-compat-data": "5.2.42", + "@mdn/browser-compat-data": "5.3.14", "addons-moz-compare": "1.3.0", - "addons-scanner-utils": "8.5.0", + "addons-scanner-utils": "9.3.0", "ajv": "8.12.0", "chalk": "4.1.2", "cheerio": "1.0.0-rc.12", "columnify": "1.6.0", "common-tags": "1.8.2", "deepmerge": "4.3.1", - "eslint": "8.36.0", + "eslint": "8.48.0", "eslint-plugin-no-unsanitized": "4.0.2", - "eslint-visitor-keys": "3.3.0", - "espree": "9.5.0", + "eslint-visitor-keys": "3.4.3", + "espree": "9.6.1", "esprima": "4.0.1", "fast-json-patch": "3.1.1", - "glob": "9.3.0", + "glob": "10.3.4", "image-size": "1.0.2", "is-mergeable-object": "1.1.1", "jed": "1.1.1", "json-merge-patch": "1.0.2", "os-locale": "5.0.0", - "pino": "8.11.0", - "postcss": "8.4.21", + "pino": "8.15.0", + "postcss": "8.4.29", "relaxed-json": "1.0.3", - "semver": "7.3.8", + "semver": "7.5.4", "sha.js": "2.4.11", "source-map-support": "0.5.21", "tosource": "1.0.0", "upath": "2.0.1", - "yargs": "17.7.1", + "yargs": "17.7.2", "yauzl": "2.10.0" }, "bin": { "addons-linter": "bin/addons-linter" }, "engines": { - "node": ">=12.21.0" + "node": ">=16.0.0" } }, - "node_modules/addons-linter/node_modules/@eslint/js": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", - "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", + "node_modules/addons-linter/node_modules/addons-scanner-utils": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/addons-scanner-utils/-/addons-scanner-utils-9.3.0.tgz", + "integrity": "sha512-YZWzNpP+em650XlZNH7NbTUcHJXqC0ihLEgwn17GGTqervyChqQffd9sm/QXNur0dmj7Ks1mD77iTg9XcJw64A==", "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "dependencies": { + "@types/yauzl": "2.10.0", + "common-tags": "1.8.2", + "first-chunk-stream": "3.0.0", + "strip-bom-stream": "4.0.0", + "upath": "2.0.1", + "yauzl": "2.10.0" + }, + "peerDependencies": { + "body-parser": "1.20.2", + "express": "4.18.2", + "node-fetch": "2.6.11", + "safe-compare": "1.1.4" + }, + "peerDependenciesMeta": { + "body-parser": { + "optional": true + }, + "express": { + "optional": true + }, + "node-fetch": { + "optional": true + }, + "safe-compare": { + "optional": true + } } }, "node_modules/addons-linter/node_modules/ajv": { @@ -3827,12 +3909,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/addons-linter/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/addons-linter/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -3876,138 +3952,11 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/addons-linter/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/addons-linter/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/addons-linter/node_modules/eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", - "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.1", - "@eslint/js": "8.36.0", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.5.0", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/addons-linter/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/addons-linter/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/addons-linter/node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/addons-linter/node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/addons-linter/node_modules/espree": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", - "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -4015,70 +3964,28 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/addons-linter/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/addons-linter/node_modules/glob": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.0.tgz", - "integrity": "sha512-EAZejC7JvnQINayvB/7BJbpZpNOJ8Lrw2OZNEvQxe0vaLn1SuwMcfV7/MNaX8L/T0wmptBFI4YMtDvSBxYDc7w==", + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz", + "integrity": "sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "minimatch": "^7.4.1", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/addons-linter/node_modules/glob/node_modules/minimatch": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", - "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" + "bin": { + "glob": "dist/cjs/src/bin.js" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/addons-linter/node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/addons-linter/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4088,52 +3995,47 @@ "node": ">=8" } }, - "node_modules/addons-linter/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/addons-linter/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "node_modules/addons-linter/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/addons-linter/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/addons-linter/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/addons-linter/node_modules/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "p-limit": "^3.0.2" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">=10" + "node": "4.x || >=6.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, "node_modules/addons-linter/node_modules/source-map": { @@ -4155,20 +4057,6 @@ "source-map": "^0.6.0" } }, - "node_modules/addons-linter/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/addons-linter/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4181,34 +4069,32 @@ "node": ">=8" } }, - "node_modules/addons-linter/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/addons-linter/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "optional": true, + "peer": true }, - "node_modules/addons-linter/node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "node_modules/addons-linter/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/addons-linter/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "node_modules/addons-moz-compare": { @@ -4217,40 +4103,6 @@ "integrity": "sha512-/rXpQeaY0nOKhNx00pmZXdk5Mu+KhVlL3/pSBuAYwrxRrNiTvI/9xfQI8Lmm7DMMl+PDhtfAHY/0ibTpdeoQQQ==", "dev": true }, - "node_modules/addons-scanner-utils": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/addons-scanner-utils/-/addons-scanner-utils-8.5.0.tgz", - "integrity": "sha512-X35SYZRdSnxx7UZuAk+DizKihQp2Ze2c5GV+5nnRr/FFyx/fOgE3Zo8jdhzSne57PENE9w1ZVocBLJTN6UDB3g==", - "dev": true, - "dependencies": { - "@types/yauzl": "2.10.0", - "common-tags": "1.8.2", - "first-chunk-stream": "3.0.0", - "strip-bom-stream": "4.0.0", - "upath": "2.0.1", - "yauzl": "2.10.0" - }, - "peerDependencies": { - "body-parser": "1.20.2", - "express": "4.18.2", - "node-fetch": "2.6.7", - "safe-compare": "1.1.4" - }, - "peerDependenciesMeta": { - "body-parser": { - "optional": true - }, - "express": { - "optional": true - }, - "node-fetch": { - "optional": true - }, - "safe-compare": { - "optional": true - } - } - }, "node_modules/adm-zip": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", @@ -5083,18 +4935,6 @@ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", "dev": true }, - "node_modules/body": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", - "integrity": "sha512-chUsBxGRtuElD6fmw1gHLpvnKdVLK302peeFa9ZqAEk8TyzZ3fygLyUEDDPTJvL9+Bor0dIwn6ePOsRM2y0zQQ==", - "dev": true, - "dependencies": { - "continuable-cache": "^0.3.1", - "error": "^7.0.0", - "raw-body": "~1.1.0", - "safe-json-parse": "~1.0.1" - } - }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -5456,20 +5296,23 @@ } }, "node_modules/browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", + "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", "dev": true, "dependencies": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", + "elliptic": "^6.5.4", "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "parse-asn1": "^5.1.6", + "readable-stream": "^3.6.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 4" } }, "node_modules/browserify-zlib": { @@ -5631,12 +5474,6 @@ "safe-json-stringify": "~1" } }, - "node_modules/bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ==", - "dev": true - }, "node_modules/cacheable-lookup": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", @@ -6344,12 +6181,6 @@ "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", "dev": true }, - "node_modules/continuable-cache": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", - "integrity": "sha512-TF30kpKhTH8AGCG3dut0rdd/19B7Z+qCnrMoBLpyQu/2drZdNrrpcjPEoJeSVsQM+8KmWG5O56oPDjSSUsuTyA==", - "dev": true - }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", @@ -7347,15 +7178,6 @@ "integrity": "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==", "dev": true }, - "node_modules/error": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", - "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", - "dev": true, - "dependencies": { - "string-template": "~0.2.1" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -7559,15 +7381,15 @@ } }, "node_modules/eslint": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", - "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.1", - "@eslint/js": "^8.46.0", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -7578,7 +7400,7 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.2", + "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", @@ -7935,9 +7757,9 @@ } }, "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", - "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -8349,18 +8171,6 @@ "reusify": "^1.0.4" } }, - "node_modules/faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ==", - "dev": true, - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -8658,6 +8468,34 @@ "node": ">=0.10.0" } }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -8922,18 +8760,6 @@ "which": "bin/which" } }, - "node_modules/gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "dependencies": { - "globule": "^1.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -9243,52 +9069,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globule": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz", - "integrity": "sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==", - "dev": true, - "dependencies": { - "glob": "~7.1.1", - "lodash": "^4.17.21", - "minimatch": "~3.0.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/globule/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globule/node_modules/minimatch": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -9533,21 +9313,6 @@ "node": ">=0.8.0" } }, - "node_modules/grunt-contrib-watch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz", - "integrity": "sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg==", - "dev": true, - "dependencies": { - "async": "^2.6.0", - "gaze": "^1.1.0", - "lodash": "^4.17.10", - "tiny-lr": "^1.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/grunt-known-options": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz", @@ -10094,6 +9859,11 @@ "void-elements": "3.1.0" } }, + "node_modules/html5-qrcode": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/html5-qrcode/-/html5-qrcode-2.3.8.tgz", + "integrity": "sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ==" + }, "node_modules/htmlescape": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", @@ -10128,12 +9898,6 @@ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true - }, "node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -11229,6 +10993,24 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jed": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/jed/-/jed-1.1.1.tgz", @@ -12846,16 +12628,6 @@ "url": "https://github.com/sponsors/panva" } }, - "node_modules/js-sdsl": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.2.tgz", - "integrity": "sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -13387,12 +13159,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "node_modules/livereload-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", - "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", - "dev": true - }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -13775,12 +13541,12 @@ } }, "node_modules/minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/mkdirp": { @@ -14401,10 +14167,13 @@ } }, "node_modules/on-exit-leak-free": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", - "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==", - "dev": true + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.1.tgz", + "integrity": "sha512-IPTBZ175tI0sSg0ikDcCDfa5dPgcFbJgABsTHsY+Mkdm6Y2VKGuchubXSvTuu5tSPl4mqt53o3nLI74HTs8UgQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } }, "node_modules/once": { "version": "1.4.0", @@ -14882,13 +14651,14 @@ } }, "node_modules/passbolt-styleguide": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/passbolt-styleguide/-/passbolt-styleguide-4.3.0.tgz", - "integrity": "sha512-2saSWuVRfrchsIdbg5j/Sdk5yzP6ptnQMF0v+tAvbwelHb8WGqEZsUh2uoIoQU4YL5qU6v6Er9H2O1RfN/CTtg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/passbolt-styleguide/-/passbolt-styleguide-4.4.1.tgz", + "integrity": "sha512-y2pGOlEcR6C5MoDscG/ub/jS5DRrZjQuae1Z9eQrDAw8avTUh5CUVXjRVKirR9BP4xJ+/HNNYxU/4dleTDfcvA==", "dependencies": { "@testing-library/dom": "^8.11.3", "debounce-promise": "^3.1.2", "grapheme-splitter": "^1.0.4", + "html5-qrcode": "^2.3.8", "i18next": "^21.6.14", "i18next-http-backend": "^1.4.0", "ip-regex": "^4.3.0", @@ -14910,8 +14680,8 @@ "xregexp": "^4.3.0" }, "engines": { - "node": ">=16.14.0", - "npm": ">=8.3.1" + "node": ">=18.18.0", + "npm": ">=9.8.1" } }, "node_modules/passbolt-styleguide/node_modules/xregexp": { @@ -15021,15 +14791,6 @@ "node": "14 || >=16.14" } }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", - "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", @@ -15112,9 +14873,9 @@ } }, "node_modules/pino": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.11.0.tgz", - "integrity": "sha512-Z2eKSvlrl2rH8p5eveNUnTdd4AjJk8tAsLkHYZQKGHP4WTh2Gi1cOSOs3eWPqaj+niS3gj4UkoreoaWgF3ZWYg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.15.0.tgz", + "integrity": "sha512-olUADJByk4twxccmAxb1RiGKOSvddHugCV3wkqjyv+3Sooa2KLrmXrKEWOKi0XPCLasRR5jBXxioE1jxUa4KzQ==", "dev": true, "dependencies": { "atomic-sleep": "^1.0.0", @@ -15219,9 +14980,9 @@ } }, "node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "dev": true, "funding": [ { @@ -15231,10 +14992,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -15708,25 +15473,6 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/raw-body": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", - "integrity": "sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg==", - "dev": true, - "dependencies": { - "bytes": "1", - "string_decoder": "0.10" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/raw-body/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "dev": true - }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -16463,12 +16209,6 @@ } ] }, - "node_modules/safe-json-parse": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", - "integrity": "sha512-o0JmTu17WGUaUOHa1l0FPGXKBfijbxK6qoHzlkihsDXxzBHvJcA7zgviKR92Xs841rX9pK16unfphLq0/KqX7A==", - "dev": true - }, "node_modules/safe-json-stringify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", @@ -16795,9 +16535,9 @@ } }, "node_modules/sonic-boom": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", - "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.5.0.tgz", + "integrity": "sha512-02A0wEmj4d3aEIW/Sp6LMP1dNcG5cYmQPjhgtytIXa9tNmFZx3ragUPFmyBdgdM0yJJVSWwlLLEVHgrYfA0wtQ==", "dev": true, "dependencies": { "atomic-sleep": "^1.0.0" @@ -17125,12 +16865,6 @@ "node": ">=10" } }, - "node_modules/string-template": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==", - "dev": true - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -17148,6 +16882,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/string-width/node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -17250,6 +17005,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -17654,29 +17422,6 @@ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" }, - "node_modules/tiny-lr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", - "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", - "dev": true, - "dependencies": { - "body": "^5.1.0", - "debug": "^3.1.0", - "faye-websocket": "~0.10.0", - "livereload-js": "^2.3.0", - "object-assign": "^4.1.0", - "qs": "^6.4.0" - } - }, - "node_modules/tiny-lr/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/tiny-warning": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", @@ -18643,14 +18388,14 @@ } }, "node_modules/web-ext": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-7.6.2.tgz", - "integrity": "sha512-xlxbzgFBIS/UWWlvWxyR1PIqRRzDj1cutoHh+VZu4ZTcJTfv35KVdKkLRZv4PQwHu4dg8VfTg7WEcNP4QLaaFQ==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-7.8.0.tgz", + "integrity": "sha512-ta+VjbNfK5q0pqRmrlH/DQMdJ89sAJFfgcvpqoUoe2gPxLu18zZ5mIakGvyjmfjrkJLOZ2NCNF7suj/qd6xaEQ==", "dev": true, "dependencies": { "@babel/runtime": "7.21.0", "@devicefarmer/adbkit": "3.2.3", - "addons-linter": "5.32.0", + "addons-linter": "6.13.0", "bunyan": "1.8.15", "camelcase": "7.0.1", "chrome-launcher": "0.15.1", @@ -19028,29 +18773,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", @@ -19202,6 +18924,77 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -19486,11 +19279,12 @@ } }, "@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "requires": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" } }, "@babel/compat-data": { @@ -19500,26 +19294,34 @@ "dev": true }, "@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", + "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", "dev": true, "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "7.5.3" + }, + "dependencies": { + "convert-source-map": { + "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 + } } }, "@babel/eslint-parser": { @@ -19534,12 +19336,12 @@ } }, "@babel/generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", - "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "requires": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -19564,13 +19366,13 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", - "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "requires": { "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", "browserslist": "^4.21.9", "lru-cache": "^5.1.1", "semver": "7.5.3" @@ -19590,7 +19392,7 @@ "@babel/helper-replace-supers": "^7.22.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" + "semver": "7.5.3" } }, "@babel/helper-create-regexp-features-plugin": { @@ -19601,7 +19403,7 @@ "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", - "semver": "^6.3.1" + "semver": "7.5.3" } }, "@babel/helper-define-polyfill-provider": { @@ -19618,19 +19420,19 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { @@ -19652,25 +19454,25 @@ } }, "@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" } }, "@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.20" } }, "@babel/helper-optimise-call-expression": { @@ -19744,14 +19546,14 @@ "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, "@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "dev": true }, "@babel/helper-wrap-function": { @@ -19766,30 +19568,30 @@ } }, "@babel/helpers": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", - "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", "dev": true, "requires": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0" } }, "@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "requires": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", - "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -20697,42 +20499,42 @@ } }, "@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.22.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", - "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/types": "^7.22.5", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -20807,9 +20609,9 @@ "dev": true }, "@eslint/eslintrc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", - "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -20830,9 +20632,9 @@ "dev": true }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.22.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", + "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -20856,9 +20658,9 @@ } }, "@eslint/js": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", - "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true }, "@fluent/syntax": { @@ -20896,6 +20698,37 @@ "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", "requires": {} }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "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, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -21454,9 +21287,9 @@ } }, "@mdn/browser-compat-data": { - "version": "5.2.42", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.2.42.tgz", - "integrity": "sha512-CD/2ai1W45cDN/zN2AcYduDavU+nq9aStyQizi4MHxnwkRvS/H24WIjgc1qD8CISoqXa8AAIe+A+zpWxwV7a2Q==", + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.3.14.tgz", + "integrity": "sha512-Y9XQrphVcE6u9xMm+gIqN86opbU/5s2W1pdPyKRyFV5B7+2jWM2gLI5JpfhZncaoDKvhy6FYwK04aCz5UM/bTQ==", "dev": true }, "@nicolo-ribaudo/eslint-scope-5-internals": { @@ -21494,6 +21327,13 @@ "fastq": "^1.6.0" } }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -21876,7 +21716,7 @@ "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", + "semver": "7.5.3", "tsutils": "^3.21.0" } }, @@ -21893,7 +21733,7 @@ "@typescript-eslint/types": "5.62.0", "@typescript-eslint/typescript-estree": "5.62.0", "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "semver": "7.5.3" } }, "@typescript-eslint/visitor-keys": { @@ -22185,50 +22025,58 @@ "dev": true }, "addons-linter": { - "version": "5.32.0", - "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-5.32.0.tgz", - "integrity": "sha512-Lf6oOyw8X9z5BMd9xhQwSbPlN2PUlzDLnYLAVT5lkrgXEx0fO9hRk4JRxWZ8+rFGz+mCIA2TTClZF2f+MKgJQA==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-6.13.0.tgz", + "integrity": "sha512-vYgDXl8aLmN1zU4HmsQdG6tUFByg499mHnTEMWDUbSkoYDq3koTne08EsqU6sD+o814u8FxclQP7580L0g/tPQ==", "dev": true, "requires": { "@fluent/syntax": "0.19.0", - "@mdn/browser-compat-data": "5.2.42", + "@mdn/browser-compat-data": "5.3.14", "addons-moz-compare": "1.3.0", - "addons-scanner-utils": "8.5.0", + "addons-scanner-utils": "9.3.0", "ajv": "8.12.0", "chalk": "4.1.2", "cheerio": "1.0.0-rc.12", "columnify": "1.6.0", "common-tags": "1.8.2", "deepmerge": "4.3.1", - "eslint": "8.36.0", + "eslint": "8.48.0", "eslint-plugin-no-unsanitized": "4.0.2", - "eslint-visitor-keys": "3.3.0", - "espree": "9.5.0", + "eslint-visitor-keys": "3.4.3", + "espree": "9.6.1", "esprima": "4.0.1", "fast-json-patch": "3.1.1", - "glob": "9.3.0", + "glob": "10.3.4", "image-size": "1.0.2", "is-mergeable-object": "1.1.1", "jed": "1.1.1", "json-merge-patch": "1.0.2", "os-locale": "5.0.0", - "pino": "8.11.0", - "postcss": "8.4.21", + "pino": "8.15.0", + "postcss": "8.4.29", "relaxed-json": "1.0.3", - "semver": "7.3.8", + "semver": "7.5.3", "sha.js": "2.4.11", "source-map-support": "0.5.21", "tosource": "1.0.0", "upath": "2.0.1", - "yargs": "17.7.1", + "yargs": "17.7.2", "yauzl": "2.10.0" }, "dependencies": { - "@eslint/js": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", - "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", - "dev": true + "addons-scanner-utils": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/addons-scanner-utils/-/addons-scanner-utils-9.3.0.tgz", + "integrity": "sha512-YZWzNpP+em650XlZNH7NbTUcHJXqC0ihLEgwn17GGTqervyChqQffd9sm/QXNur0dmj7Ks1mD77iTg9XcJw64A==", + "dev": true, + "requires": { + "@types/yauzl": "2.10.0", + "common-tags": "1.8.2", + "first-chunk-stream": "3.0.0", + "strip-bom-stream": "4.0.0", + "upath": "2.0.1", + "yauzl": "2.10.0" + } }, "ajv": { "version": "8.12.0", @@ -22251,12 +22099,6 @@ "color-convert": "^2.0.1" } }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -22291,153 +22133,23 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", - "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.1", - "@eslint/js": "8.36.0", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.5.0", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, - "eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, - "espree": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", - "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, "glob": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.0.tgz", - "integrity": "sha512-EAZejC7JvnQINayvB/7BJbpZpNOJ8Lrw2OZNEvQxe0vaLn1SuwMcfV7/MNaX8L/T0wmptBFI4YMtDvSBxYDc7w==", + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz", + "integrity": "sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "minimatch": "^7.4.1", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - }, - "dependencies": { - "minimatch": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", - "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" } }, "has-flag": { @@ -22446,37 +22158,30 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "requires": { - "p-locate": "^5.0.0" + "brace-expansion": "^2.0.1" } }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", "dev": true, + "optional": true, + "peer": true, "requires": { - "p-limit": "^3.0.2" + "whatwg-url": "^5.0.0" } }, "source-map": { @@ -22495,17 +22200,6 @@ "source-map": "^0.6.0" } }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -22515,25 +22209,32 @@ "has-flag": "^4.0.0" } }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "optional": true, + "peer": true }, - "yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "optional": true, + "peer": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, + "optional": true, + "peer": true, "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } } } @@ -22544,20 +22245,6 @@ "integrity": "sha512-/rXpQeaY0nOKhNx00pmZXdk5Mu+KhVlL3/pSBuAYwrxRrNiTvI/9xfQI8Lmm7DMMl+PDhtfAHY/0ibTpdeoQQQ==", "dev": true }, - "addons-scanner-utils": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/addons-scanner-utils/-/addons-scanner-utils-8.5.0.tgz", - "integrity": "sha512-X35SYZRdSnxx7UZuAk+DizKihQp2Ze2c5GV+5nnRr/FFyx/fOgE3Zo8jdhzSne57PENE9w1ZVocBLJTN6UDB3g==", - "dev": true, - "requires": { - "@types/yauzl": "2.10.0", - "common-tags": "1.8.2", - "first-chunk-stream": "3.0.0", - "strip-bom-stream": "4.0.0", - "upath": "2.0.1", - "yauzl": "2.10.0" - } - }, "adm-zip": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", @@ -23205,18 +22892,6 @@ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", "dev": true }, - "body": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", - "integrity": "sha512-chUsBxGRtuElD6fmw1gHLpvnKdVLK302peeFa9ZqAEk8TyzZ3fygLyUEDDPTJvL9+Bor0dIwn6ePOsRM2y0zQQ==", - "dev": true, - "requires": { - "continuable-cache": "^0.3.1", - "error": "^7.0.0", - "raw-body": "~1.1.0", - "safe-json-parse": "~1.0.1" - } - }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -23556,20 +23231,20 @@ } }, "browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", + "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", "dev": true, "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", + "elliptic": "^6.5.4", "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "parse-asn1": "^5.1.6", + "readable-stream": "^3.6.2", + "safe-buffer": "^5.2.1" } }, "browserify-zlib": { @@ -23660,12 +23335,6 @@ "safe-json-stringify": "~1" } }, - "bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ==", - "dev": true - }, "cacheable-lookup": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", @@ -24243,12 +23912,6 @@ "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", "dev": true }, - "continuable-cache": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", - "integrity": "sha512-TF30kpKhTH8AGCG3dut0rdd/19B7Z+qCnrMoBLpyQu/2drZdNrrpcjPEoJeSVsQM+8KmWG5O56oPDjSSUsuTyA==", - "dev": true - }, "convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", @@ -24377,7 +24040,7 @@ "dev": true, "requires": { "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", + "browserify-sign": "4.2.2", "create-ecdh": "^4.0.0", "create-hash": "^1.1.0", "create-hmac": "^1.1.0", @@ -25055,15 +24718,6 @@ "integrity": "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==", "dev": true }, - "error": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", - "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", - "dev": true, - "requires": { - "string-template": "~0.2.1" - } - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -25224,15 +24878,15 @@ } }, "eslint": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", - "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.1", - "@eslint/js": "^8.46.0", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -25243,7 +24897,7 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.2", + "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", @@ -25325,9 +24979,9 @@ } }, "eslint-visitor-keys": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", - "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "find-up": { @@ -25816,15 +25470,6 @@ "reusify": "^1.0.4" } }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ==", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, "fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -26065,6 +25710,24 @@ "for-in": "^1.0.1" } }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -26275,15 +25938,6 @@ } } }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "requires": { - "globule": "^1.0.0" - } - }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -26380,7 +26034,7 @@ "requires": { "extend": "^3.0.0", "glob": "^7.1.1", - "glob-parent": "^3.1.0", + "glob-parent": "5.1.2", "is-negated-glob": "^1.0.0", "ordered-read-streams": "^1.0.0", "pumpify": "^1.3.5", @@ -26391,7 +26045,8 @@ }, "dependencies": { "glob-parent": { - "version": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { @@ -26523,42 +26178,6 @@ "slash": "^3.0.0" } }, - "globule": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz", - "integrity": "sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==", - "dev": true, - "requires": { - "glob": "~7.1.1", - "lodash": "^4.17.21", - "minimatch": "~3.0.2" - }, - "dependencies": { - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, "gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -26780,18 +26399,6 @@ } } }, - "grunt-contrib-watch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz", - "integrity": "sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg==", - "dev": true, - "requires": { - "async": "^2.6.0", - "gaze": "^1.1.0", - "lodash": "^4.17.10", - "tiny-lr": "^1.1.1" - } - }, "grunt-known-options": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz", @@ -27196,6 +26803,11 @@ "void-elements": "3.1.0" } }, + "html5-qrcode": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/html5-qrcode/-/html5-qrcode-2.3.8.tgz", + "integrity": "sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ==" + }, "htmlescape": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", @@ -27220,12 +26832,6 @@ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, - "http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true - }, "http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -27951,7 +27557,7 @@ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "requires": { - "semver": "^7.5.3" + "semver": "7.5.3" } }, "supports-color": { @@ -27994,6 +27600,16 @@ "istanbul-lib-report": "^3.0.0" } }, + "jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "jed": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/jed/-/jed-1.1.1.tgz", @@ -29194,12 +28810,6 @@ "integrity": "sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ==", "dev": true }, - "js-sdsl": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.2.tgz", - "integrity": "sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -29347,7 +28957,7 @@ "jws": "^3.2.2", "lodash": "^4.17.21", "ms": "^2.1.1", - "semver": "^7.3.8" + "semver": "7.5.3" } }, "jsprim": { @@ -29652,12 +29262,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "livereload-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", - "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", - "dev": true - }, "loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -29964,9 +29568,9 @@ "dev": true }, "minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "dev": true }, "mkdirp": { @@ -30438,9 +30042,9 @@ } }, "on-exit-leak-free": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", - "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.1.tgz", + "integrity": "sha512-IPTBZ175tI0sSg0ikDcCDfa5dPgcFbJgABsTHsY+Mkdm6Y2VKGuchubXSvTuu5tSPl4mqt53o3nLI74HTs8UgQ==", "dev": true }, "once": { @@ -30722,7 +30326,7 @@ "got": "^12.1.0", "registry-auth-token": "^5.0.1", "registry-url": "^6.0.0", - "semver": "^7.3.7" + "semver": "7.5.3" } }, "pako": { @@ -30816,13 +30420,14 @@ } }, "passbolt-styleguide": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/passbolt-styleguide/-/passbolt-styleguide-4.3.0.tgz", - "integrity": "sha512-2saSWuVRfrchsIdbg5j/Sdk5yzP6ptnQMF0v+tAvbwelHb8WGqEZsUh2uoIoQU4YL5qU6v6Er9H2O1RfN/CTtg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/passbolt-styleguide/-/passbolt-styleguide-4.4.1.tgz", + "integrity": "sha512-y2pGOlEcR6C5MoDscG/ub/jS5DRrZjQuae1Z9eQrDAw8avTUh5CUVXjRVKirR9BP4xJ+/HNNYxU/4dleTDfcvA==", "requires": { "@testing-library/dom": "^8.11.3", "debounce-promise": "^3.1.2", "grapheme-splitter": "^1.0.4", + "html5-qrcode": "^2.3.8", "i18next": "^21.6.14", "i18next-http-backend": "^1.4.0", "ip-regex": "^4.3.0", @@ -30925,12 +30530,6 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", "dev": true - }, - "minipass": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", - "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", - "dev": true } } }, @@ -31003,9 +30602,9 @@ "dev": true }, "pino": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.11.0.tgz", - "integrity": "sha512-Z2eKSvlrl2rH8p5eveNUnTdd4AjJk8tAsLkHYZQKGHP4WTh2Gi1cOSOs3eWPqaj+niS3gj4UkoreoaWgF3ZWYg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.15.0.tgz", + "integrity": "sha512-olUADJByk4twxccmAxb1RiGKOSvddHugCV3wkqjyv+3Sooa2KLrmXrKEWOKi0XPCLasRR5jBXxioE1jxUa4KzQ==", "dev": true, "requires": { "atomic-sleep": "^1.0.0", @@ -31083,12 +30682,12 @@ "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==" }, "postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "dev": true, "requires": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } @@ -31467,24 +31066,6 @@ "safe-buffer": "^5.1.0" } }, - "raw-body": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", - "integrity": "sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg==", - "dev": true, - "requires": { - "bytes": "1", - "string_decoder": "0.10" - }, - "dependencies": { - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "dev": true - } - } - }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -31853,7 +31434,7 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", + "tough-cookie": "4.1.3", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" }, @@ -32043,12 +31624,6 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, - "safe-json-parse": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", - "integrity": "sha512-o0JmTu17WGUaUOHa1l0FPGXKBfijbxK6qoHzlkihsDXxzBHvJcA7zgviKR92Xs841rX9pK16unfphLq0/KqX7A==", - "dev": true - }, "safe-json-stringify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", @@ -32149,7 +31724,7 @@ "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", "dev": true, "requires": { - "semver": "^7.3.5" + "semver": "7.5.3" } }, "serialize-javascript": { @@ -32304,9 +31879,9 @@ "dev": true }, "sonic-boom": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", - "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.5.0.tgz", + "integrity": "sha512-02A0wEmj4d3aEIW/Sp6LMP1dNcG5cYmQPjhgtytIXa9tNmFZx3ragUPFmyBdgdM0yJJVSWwlLLEVHgrYfA0wtQ==", "dev": true, "requires": { "atomic-sleep": "^1.0.0" @@ -32597,12 +32172,6 @@ "strip-ansi": "^6.0.0" } }, - "string-template": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==", - "dev": true - }, "string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -32631,6 +32200,25 @@ } } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + } + } + }, "string.prototype.matchall": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", @@ -32688,6 +32276,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -33007,31 +32604,6 @@ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" }, - "tiny-lr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", - "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", - "dev": true, - "requires": { - "body": "^5.1.0", - "debug": "^3.1.0", - "faye-websocket": "~0.10.0", - "livereload-js": "^2.3.0", - "object-assign": "^4.1.0", - "qs": "^6.4.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, "tiny-warning": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", @@ -33436,7 +33008,7 @@ "is-yarn-global": "^0.4.0", "latest-version": "^7.0.0", "pupa": "^3.1.0", - "semver": "^7.3.7", + "semver": "7.5.3", "semver-diff": "^4.0.0", "xdg-basedir": "^5.1.0" }, @@ -33796,14 +33368,14 @@ } }, "web-ext": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-7.6.2.tgz", - "integrity": "sha512-xlxbzgFBIS/UWWlvWxyR1PIqRRzDj1cutoHh+VZu4ZTcJTfv35KVdKkLRZv4PQwHu4dg8VfTg7WEcNP4QLaaFQ==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-7.8.0.tgz", + "integrity": "sha512-ta+VjbNfK5q0pqRmrlH/DQMdJ89sAJFfgcvpqoUoe2gPxLu18zZ5mIakGvyjmfjrkJLOZ2NCNF7suj/qd6xaEQ==", "dev": true, "requires": { "@babel/runtime": "7.21.0", "@devicefarmer/adbkit": "3.2.3", - "addons-linter": "5.32.0", + "addons-linter": "6.13.0", "bunyan": "1.8.15", "camelcase": "7.0.1", "chrome-launcher": "0.15.1", @@ -34064,23 +33636,6 @@ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true - }, "whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", @@ -34216,6 +33771,60 @@ } } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 33d49c18..98633adf 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { "name": "passbolt-browser-extension", - "version": "4.3.1", + "version": "4.4.0-rc.0", "license": "AGPL-3.0", "copyright": "Copyright 2022 Passbolt SA", "description": "Passbolt web extension for the open source password manager for teams", "homepage": "https://www.passbolt.com", "repository": "https://github.com/passbolt/passbolt_browser_extension", "engines": { - "node": ">=16.14.0", - "npm": ">=8.3.1" + "node": ">=18.18.0", + "npm": ">=9.8.1" }, "dependencies": { "await-lock": "^2.1.0", @@ -21,7 +21,7 @@ "locutus": "~2.0.9", "openpgp": "5.2.1", "papaparse": "^5.2.0", - "passbolt-styleguide": "^4.3.0", + "passbolt-styleguide": "^4.4.1", "react": "17.0.2", "react-dom": "17.0.2", "secrets-passbolt": "github:passbolt/secrets.js#v2.0.1", @@ -30,7 +30,7 @@ "xregexp": "~5.1.0" }, "devDependencies": { - "@babel/core": "^7.22.9", + "@babel/core": "^7.23.2", "@babel/eslint-parser": "^7.22.9", "@babel/plugin-transform-runtime": "^7.22.9", "@babel/preset-env": "^7.22.9", @@ -47,7 +47,6 @@ "grunt-browserify": "^6.0.0", "grunt-contrib-clean": "^2.0.0", "grunt-contrib-copy": "^1.0.0", - "grunt-contrib-watch": "^1.1.0", "grunt-shell": "^4.0.0", "i18next-parser": "^6.5.0", "jest": "^29.6.2", @@ -59,7 +58,7 @@ "jest-webextension-mock": "^3.8.9", "text-encoding-utf-8": "^1.0.2", "uuid": "^8.3.2", - "web-ext": "7.6.2", + "web-ext": "^7.8.0", "webpack": "^5.76.3", "webpack-cli": "^4.9.1" }, @@ -69,7 +68,10 @@ "glob-stream": { "glob-parent": "5.1.2" }, - "firefox-profile": "4.3.2" + "firefox-profile": "4.3.2", + "crypto-browserify": { + "browserify-sign": "4.2.2" + } }, "scripts": { "build": "npx grunt build", @@ -91,6 +93,13 @@ "dev:build:web-accessible-resources": "npm run dev:build:web-accessible-resources:app; npm run dev:build:web-accessible-resources:browser-integration", "dev:build:web-accessible-resources:app": "webpack --env debug=true --config webpack-data.config.js; webpack --env debug=true --config webpack-data.download.config.js", "dev:build:web-accessible-resources:browser-integration": "webpack --env debug=true --config webpack-data.in-form-call-to-action.config.js; webpack --env debug=true --config webpack-data.in-form-menu.config.js", + "dev:watch:build:background-page": "npm run dev:build:background-page -- -w", + "dev:watch:build:service-worker": "npm run dev:build:service-worker -- -w", + "dev:watch:build:content-scripts:app": "npm run dev:build:content-scripts:app -- -w", + "dev:watch:build:content-scripts:browser-integration": "npm run dev:build:content-scripts:browser-integration -- -w", + "dev:watch:build:content-scripts:public-website": "npm run dev:build:content-scripts:public-website -- -w", + "dev:watch:build:web-accessible-resources:app": "npm run dev:build:web-accessible-resources:app -- -w", + "dev:watch:build:web-accessible-resources:browser-integration": "npm run dev:build:web-accessible-resources:browser-integration -- -w", "i18n:externalize": "i18next -c ./i18next-parser.config.js", "test": "npm run test:unit", "test:unit": "jest --no-cache ./src/all/ ./src/chrome-mv3/ --maxWorkers=4", diff --git a/src/all/background_page/controller/exportAccount/exportDesktopAccountController.js b/src/all/background_page/controller/exportAccount/exportDesktopAccountController.js index 39b45040..ad0a8529 100644 --- a/src/all/background_page/controller/exportAccount/exportDesktopAccountController.js +++ b/src/all/background_page/controller/exportAccount/exportDesktopAccountController.js @@ -16,6 +16,9 @@ import DesktopTransferModel from "../../model/desktopTransfer/desktopTransferMod import FileService from "../../service/file/fileService"; import GetPassphraseService from "../../service/passphrase/getPassphraseService"; import {Buffer} from 'buffer'; +import DecryptPrivateKeyService from "../../service/crypto/decryptPrivateKeyService"; +import SignMessageService from "../../service/crypto/signMessageService"; +import {OpenpgpAssertion} from "../../utils/openpgp/openpgpAssertions"; const PUBLIC_FILENAME = "account-kit.passbolt"; const MIME_TYPE_TEXT_PLAIN = "application/passbolt"; @@ -55,10 +58,15 @@ class ExportDesktopAccountController { * @return {Promise} */ async exec() { - await this.getPassphraseService.getPassphrase(this.worker); + const passphrase = await this.getPassphraseService.getPassphrase(this.worker); const accountKit = await this.desktopTransferModel.getAccountKit(this.account); - const accountKitStringify = JSON.stringify(accountKit.toDto()); - const fileContent = Buffer.from(accountKitStringify).toString('base64'); + const accountKitDto = accountKit.toDto(); + const accountKitMessage = await OpenpgpAssertion.createMessageOrFail(JSON.stringify(accountKitDto)); + const privateKeyForSigning = await OpenpgpAssertion.readKeyOrFail(accountKitDto["user_private_armored_key"]); + const decryptedPrivateKey = await DecryptPrivateKeyService.decrypt(privateKeyForSigning, passphrase); + const signedAccountKit = await SignMessageService.sign(accountKitMessage, [decryptedPrivateKey]); + + const fileContent = Buffer.from(signedAccountKit).toString('base64'); await FileService.saveFile(PUBLIC_FILENAME, fileContent, MIME_TYPE_TEXT_PLAIN, this.worker.tab.id); } } diff --git a/src/all/background_page/controller/exportAccount/exportDesktopAccountController.test.js b/src/all/background_page/controller/exportAccount/exportDesktopAccountController.test.js index 2474f76b..aecde8f2 100644 --- a/src/all/background_page/controller/exportAccount/exportDesktopAccountController.test.js +++ b/src/all/background_page/controller/exportAccount/exportDesktopAccountController.test.js @@ -17,17 +17,19 @@ import ExportDesktopAccountController from "./exportDesktopAccountController"; import {requestId, worker} from "./exportDesktopAccountController.test.data"; import FileService from "../../service/file/fileService"; import GetLegacyAccountService from "../../service/account/getLegacyAccountService"; -import AccountKitEntity from "../../model/entity/account/accountKitEntity"; import {Buffer} from 'buffer'; +import {pgpKeys} from "../../../../../test/fixtures/pgpKeys/keys"; +import DecryptPrivateKeyService from "../../service/crypto/decryptPrivateKeyService"; +import SignMessageService from "../../service/crypto/signMessageService"; +import {OpenpgpAssertion} from "../../utils/openpgp/openpgpAssertions"; describe("ExportDesktopAccountController", () => { - const accountDto = new AccountEntity(defaultAccountDto()); - const controller = new ExportDesktopAccountController(worker, requestId, accountDto); + const account = new AccountEntity(defaultAccountDto()); + const controller = new ExportDesktopAccountController(worker, requestId, account); beforeEach(() => { - jest.spyOn(controller.getPassphraseService, "getPassphrase").mockImplementation(() => true); - jest.spyOn(GetLegacyAccountService, "get").mockImplementation(() => accountDto); - jest.spyOn(FileService, "saveFile").mockImplementation(jest.fn()); + jest.spyOn(controller.getPassphraseService, "getPassphrase").mockImplementation(() => pgpKeys.ada.passphrase); + jest.spyOn(GetLegacyAccountService, "get").mockImplementation(() => account); jest.spyOn(controller.desktopTransferModel, "getAccountKit"); }); @@ -36,6 +38,7 @@ describe("ExportDesktopAccountController", () => { expect.assertions(3); //Simulate an error to check is the other methods have not been called jest.spyOn(controller.getPassphraseService, "getPassphrase").mockRejectedValue(() => new Error()); + jest.spyOn(FileService, "saveFile").mockImplementation(jest.fn()); try { await controller.exec(); @@ -45,20 +48,27 @@ describe("ExportDesktopAccountController", () => { expect(controller.desktopTransferModel.getAccountKit).not.toHaveBeenCalled(); expect(FileService.saveFile).not.toHaveBeenCalled(); }); - - it("Should save the account kit.", async() => { - expect.assertions(2); - await controller.exec(); - - const accountToExport = new AccountKitEntity(accountDto.toDto({ - user_private_armored_key: true, - security_token: true + it("Should save the signed account kit.", async() => { + expect.assertions(5); + + let resultSignedAccountKit; + jest.spyOn(DecryptPrivateKeyService, "decrypt"); + jest.spyOn(SignMessageService, "sign"); + jest.spyOn(FileService, "saveFile").mockImplementation(jest.fn((_, content) => { + resultSignedAccountKit = content; })); - const base64Content = Buffer.from(JSON.stringify(accountToExport.toDto())).toString('base64'); + await controller.exec(); + expect(SignMessageService.sign).toHaveBeenCalled(); + expect(DecryptPrivateKeyService.decrypt).toHaveBeenCalled(); expect(controller.desktopTransferModel.getAccountKit).toHaveBeenCalled(); - expect(FileService.saveFile).toHaveBeenCalledWith("account-kit.passbolt", base64Content, "application/passbolt", worker.tab.id); + expect(FileService.saveFile).toHaveBeenCalledWith("account-kit.passbolt", expect.anything(), "application/passbolt", worker.tab.id); + // Assert the account kit output. + const signedResultAccountKit = Buffer.from(resultSignedAccountKit, "base64").toString(); + const serializedResultAccountKit = await OpenpgpAssertion.readMessageOrFail(signedResultAccountKit); + const resultAccountKitDto = JSON.parse(serializedResultAccountKit.getText()); + expect(resultAccountKitDto.user_id).toEqual(account.userId); }); }); @@ -91,3 +101,4 @@ describe("ExportDesktopAccountController", () => { }); }); }); + diff --git a/src/all/background_page/controller/mfaSetup/MfaSetupRemoveProviderController.js b/src/all/background_page/controller/mfaSetup/MfaSetupRemoveProviderController.js new file mode 100644 index 00000000..58e14c67 --- /dev/null +++ b/src/all/background_page/controller/mfaSetup/MfaSetupRemoveProviderController.js @@ -0,0 +1,58 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import MfaProviderEntity from "../../model/entity/mfa/mfaProviderEntity"; +import MultiFactorAuthenticationModel from "../../model/multiFactorAuthentication/multiFactorAuthenticationModel"; + + +class MfaSetupRemoveProviderController { + /** + * MfaSetupVerifyOtpCodeController constructor + * @param {Worker} worker + * @param {string} requestId uuid + */ + constructor(worker, requestId, apiClientOptions) { + this.worker = worker; + this.requestId = requestId; + this.multiFactorAuthenticationModel = new MultiFactorAuthenticationModel(apiClientOptions); + } + + /** + * Controller executor. + * @param {string} provider the provider + * @returns {Promise} + */ + async _exec(providerDto) { + try { + await this.exec(providerDto); + this.worker.port.emit(this.requestId, "SUCCESS"); + } catch (error) { + console.error(error); + this.worker.port.emit(this.requestId, 'ERROR', error); + } + } + + /** + * Check and save the otp code + * @param {string} provider the provider + * @throws {Error} if the provider is missing + * @throws {TypeError} if the provider is not part of the enum + */ + async exec(providerDto) { + const providerEntity = new MfaProviderEntity(providerDto); + await this.multiFactorAuthenticationModel.removeProvider(providerEntity); + } +} + +export default MfaSetupRemoveProviderController; diff --git a/src/all/background_page/controller/mfaSetup/MfaSetupRemoveProviderController.test.js b/src/all/background_page/controller/mfaSetup/MfaSetupRemoveProviderController.test.js new file mode 100644 index 00000000..289c16bb --- /dev/null +++ b/src/all/background_page/controller/mfaSetup/MfaSetupRemoveProviderController.test.js @@ -0,0 +1,56 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import {enableFetchMocks} from "jest-fetch-mock"; +import {mockApiResponse} from "../../../../../test/mocks/mockApiResponse"; +import {defaultApiClientOptions} from "../../service/api/apiClient/apiClientOptions.test.data"; +import MfaSetupRemoveProviderController from "./MfaSetupRemoveProviderController"; +import {defaultMfaProviderData} from "../../model/entity/mfa/mfaProviderEntity.test.data"; +import EntityValidationError from "passbolt-styleguide/src/shared/models/entity/abstract/entityValidationError"; +import MfaProviderEntity from "../../model/entity/mfa/mfaProviderEntity"; + +beforeEach(() => { + enableFetchMocks(); +}); + + +describe("MfaSetupRemoveProviderController", () => { + let controller; + + beforeEach(() => { + controller = new MfaSetupRemoveProviderController(null, null, defaultApiClientOptions()); + }); + + it("Should remove the totp", async() => { + expect.assertions(1); + jest.spyOn(controller.multiFactorAuthenticationModel, "removeProvider"); + + fetch.doMock(() => mockApiResponse({})); + + await controller.exec(defaultMfaProviderData()); + + expect(controller.multiFactorAuthenticationModel.removeProvider).toHaveBeenCalledWith(new MfaProviderEntity(defaultMfaProviderData())); + }); + + it("Should validate the totp with entity", async() => { + expect.assertions(2); + + try { + await controller.exec({}); + } catch (error) { + expect(error).toBeInstanceOf(EntityValidationError); + expect(error.hasError('provider', 'required')).toBeTruthy(); + } + }); +}); diff --git a/src/all/background_page/controller/mfaSetup/MfaSetupVerifyOtpCodeController.js b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyOtpCodeController.js new file mode 100644 index 00000000..145c186e --- /dev/null +++ b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyOtpCodeController.js @@ -0,0 +1,56 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import MfaSetupTotpEntity from "../../model/entity/mfa/mfaSetupTotpEntity"; +import MultiFactorAuthenticationModel from "../../model/multiFactorAuthentication/multiFactorAuthenticationModel"; + + +class MfaSetupVerifyOtpCodeController { + /** + * MfaSetupVerifyOtpCodeController constructor + * @param {Worker} worker + * @param {string} requestId uuid + */ + constructor(worker, requestId, apiClientOptions) { + this.worker = worker; + this.requestId = requestId; + this.multiFactorAuthenticationModel = new MultiFactorAuthenticationModel(apiClientOptions); + } + + /** + * Controller executor. + * @param {Object} setupDto the totp object with uri and otp code + * @returns {Promise} + */ + async _exec(setupDto) { + try { + await this.exec(setupDto); + this.worker.port.emit(this.requestId, "SUCCESS"); + } catch (error) { + console.error(error); + this.worker.port.emit(this.requestId, 'ERROR', error); + } + } + + /** + * Check and save the otp code + * @param {Object} setupDto the totp object with uri and otp code + */ + async exec(setupDto) { + const totpEntity = new MfaSetupTotpEntity(setupDto); + await this.multiFactorAuthenticationModel.setupTotp(totpEntity); + } +} + +export default MfaSetupVerifyOtpCodeController; diff --git a/src/all/background_page/controller/mfaSetup/MfaSetupVerifyOtpCodeController.test.js b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyOtpCodeController.test.js new file mode 100644 index 00000000..144af3aa --- /dev/null +++ b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyOtpCodeController.test.js @@ -0,0 +1,57 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import {enableFetchMocks} from "jest-fetch-mock"; +import {defaultApiClientOptions} from "../../service/api/apiClient/apiClientOptions.test.data"; +import EntityValidationError from "passbolt-styleguide/src/shared/models/entity/abstract/entityValidationError"; +import MfaSetupVerifyOtpCodeController from "./MfaSetupVerifyOtpCodeController"; +import {mockApiResponse} from "../../../../../test/mocks/mockApiResponse"; +import {defaultSetupTotpData} from "../../model/entity/mfa/mfaSetupTotpEntity.test.data"; +import MfaSetupTotpEntity from "../../model/entity/mfa/mfaSetupTotpEntity"; + +beforeEach(() => { + enableFetchMocks(); +}); + + +describe("MfaSetupVerifyOtpCodeController", () => { + let controller; + + beforeEach(() => { + controller = new MfaSetupVerifyOtpCodeController(null, null, defaultApiClientOptions()); + }); + + it("Should verify the otp code", async() => { + expect.assertions(1); + jest.spyOn(controller.multiFactorAuthenticationModel, "setupTotp"); + + fetch.doMock(() => mockApiResponse({})); + + await controller.exec(defaultSetupTotpData()); + + expect(controller.multiFactorAuthenticationModel.setupTotp).toHaveBeenCalledWith(new MfaSetupTotpEntity(defaultSetupTotpData())); + }); + + it("Should validate the otp uri and code with entity", async() => { + expect.assertions(3); + + try { + await controller.exec({}); + } catch (error) { + expect(error).toBeInstanceOf(EntityValidationError); + expect(error.hasError('totp', 'required')).toBeTruthy(); + expect(error.hasError('otpProvisioningUri', 'required')).toBeTruthy(); + } + }); +}); diff --git a/src/all/background_page/controller/mfaSetup/MfaSetupVerifyProviderController.js b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyProviderController.js new file mode 100644 index 00000000..500004e8 --- /dev/null +++ b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyProviderController.js @@ -0,0 +1,58 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import MfaProviderEntity from "../../model/entity/mfa/mfaProviderEntity"; +import MultiFactorAuthenticationModel from "../../model/multiFactorAuthentication/multiFactorAuthenticationModel"; + + +class MfaSetupVerifyProviderController { + /** + * MfaSetupVerifyOtpCodeController constructor + * @param {Worker} worker + * @param {string} requestId uuid + */ + constructor(worker, requestId, apiClientOptions) { + this.worker = worker; + this.requestId = requestId; + this.multiFactorAuthenticationModel = new MultiFactorAuthenticationModel(apiClientOptions); + } + + /** + * Controller executor. + * @param {string} provider the provider + * @returns {Promise} + */ + async _exec(providerDto) { + try { + const response = await this.exec(providerDto); + this.worker.port.emit(this.requestId, "SUCCESS", response); + } catch (error) { + console.error(error); + this.worker.port.emit(this.requestId, 'ERROR', error); + } + } + + /** + * Check and save the otp code + * @param {Object} provider the provider + * @throws {Error} if the provider is missing + * @throws {TypeError} if the provider is not part of the enum + */ + async exec(providerDto) { + const providerEntity = new MfaProviderEntity(providerDto); + return await this.multiFactorAuthenticationModel.verifyProvider(providerEntity); + } +} + +export default MfaSetupVerifyProviderController; diff --git a/src/all/background_page/controller/mfaSetup/MfaSetupVerifyProviderController.test.js b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyProviderController.test.js new file mode 100644 index 00000000..1bba1d2b --- /dev/null +++ b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyProviderController.test.js @@ -0,0 +1,59 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import {enableFetchMocks} from "jest-fetch-mock"; +import {mockApiResponse} from "../../../../../test/mocks/mockApiResponse"; +import {defaultApiClientOptions} from "../../service/api/apiClient/apiClientOptions.test.data"; +import {defaultMfaProviderData} from "../../model/entity/mfa/mfaProviderEntity.test.data"; +import EntityValidationError from "passbolt-styleguide/src/shared/models/entity/abstract/entityValidationError"; +import MfaSetupVerifyProviderController from "./MfaSetupVerifyProviderController"; +import {defaultVerifyProviderData} from "../../model/entity/mfa/mfaVerifyProviderEntity.test.data"; +import MfaProviderEntity from "../../model/entity/mfa/mfaProviderEntity"; +import MfaVerifyProviderEntity from "../../model/entity/mfa/mfaVerifyProviderEntity"; + +beforeEach(() => { + enableFetchMocks(); +}); + + +describe("MfaSetupVerifyProviderController", () => { + let controller; + + beforeEach(() => { + controller = new MfaSetupVerifyProviderController(null, null, defaultApiClientOptions()); + }); + + it("Should retrieve the verify date from totp", async() => { + expect.assertions(2); + jest.spyOn(controller.multiFactorAuthenticationModel, "verifyProvider"); + + const verifyEntityParameter = new MfaProviderEntity(defaultMfaProviderData()); + fetch.doMock(() => mockApiResponse(defaultVerifyProviderData())); + const result = await controller.exec(defaultMfaProviderData()); + + expect(controller.multiFactorAuthenticationModel.verifyProvider).toHaveBeenCalledWith(verifyEntityParameter); + expect(result).toEqual(new MfaVerifyProviderEntity(defaultVerifyProviderData())); + }); + + it("Should validate the mfa provider with entity", async() => { + expect.assertions(2); + + try { + await controller.exec({}); + } catch (error) { + expect(error).toBeInstanceOf(EntityValidationError); + expect(error.hasError('provider', 'required')).toBeTruthy(); + } + }); +}); diff --git a/src/all/background_page/controller/mfaSetup/MfaSetupVerifyTotpCodeController.js b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyTotpCodeController.js new file mode 100644 index 00000000..b093f332 --- /dev/null +++ b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyTotpCodeController.js @@ -0,0 +1,56 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import MfaSetupTotpEntity from "../../model/entity/mfa/mfaSetupTotpEntity"; +import MultiFactorAuthenticationModel from "../../model/multiFactorAuthentication/multiFactorAuthenticationModel"; + + +class MfaSetupVerifyTotpCodeController { + /** + * MfaSetupVerifyTotpCodeController constructor + * @param {Worker} worker + * @param {string} requestId uuid + */ + constructor(worker, requestId, apiClientOptions) { + this.worker = worker; + this.requestId = requestId; + this.multiFactorAuthenticationModel = new MultiFactorAuthenticationModel(apiClientOptions); + } + + /** + * Controller executor. + * @param {Object} setupDto the totp object with uri and otp code + * @returns {Promise} + */ + async _exec(setupDto) { + try { + await this.exec(setupDto); + this.worker.port.emit(this.requestId, "SUCCESS"); + } catch (error) { + console.error(error); + this.worker.port.emit(this.requestId, 'ERROR', error); + } + } + + /** + * Check and save the otp code + * @param {Object} setupDto the totp object with uri and otp code + */ + async exec(setupDto) { + const totpEntity = new MfaSetupTotpEntity(setupDto); + await this.multiFactorAuthenticationModel.setupTotp(totpEntity); + } +} + +export default MfaSetupVerifyTotpCodeController; diff --git a/src/all/background_page/controller/mfaSetup/MfaSetupVerifyTotpCodeController.test.js b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyTotpCodeController.test.js new file mode 100644 index 00000000..c3bed115 --- /dev/null +++ b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyTotpCodeController.test.js @@ -0,0 +1,57 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import {enableFetchMocks} from "jest-fetch-mock"; +import {defaultApiClientOptions} from "../../service/api/apiClient/apiClientOptions.test.data"; +import EntityValidationError from "passbolt-styleguide/src/shared/models/entity/abstract/entityValidationError"; +import MfaSetupVerifyTotpCodeController from "./MfaSetupVerifyTotpCodeController"; +import {mockApiResponse} from "../../../../../test/mocks/mockApiResponse"; +import {defaultSetupTotpData} from "../../model/entity/mfa/mfaSetupTotpEntity.test.data"; +import MfaSetupTotpEntity from "../../model/entity/mfa/mfaSetupTotpEntity"; + +beforeEach(() => { + enableFetchMocks(); +}); + + +describe("MfaSetupVerifyTotpCodeController", () => { + let controller; + + beforeEach(() => { + controller = new MfaSetupVerifyTotpCodeController(null, null, defaultApiClientOptions()); + }); + + it("Should verify the otp code", async() => { + expect.assertions(1); + jest.spyOn(controller.multiFactorAuthenticationModel, "setupTotp"); + + fetch.doMock(() => mockApiResponse({})); + + await controller.exec(defaultSetupTotpData()); + + expect(controller.multiFactorAuthenticationModel.setupTotp).toHaveBeenCalledWith(new MfaSetupTotpEntity(defaultSetupTotpData())); + }); + + it("Should validate the otp uri and code with entity", async() => { + expect.assertions(3); + + try { + await controller.exec({}); + } catch (error) { + expect(error).toBeInstanceOf(EntityValidationError); + expect(error.hasError('totp', 'required')).toBeTruthy(); + expect(error.hasError('otpProvisioningUri', 'required')).toBeTruthy(); + } + }); +}); diff --git a/src/all/background_page/controller/mfaSetup/MfaSetupVerifyYubikeyCodeController.js b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyYubikeyCodeController.js new file mode 100644 index 00000000..7f8f3d2b --- /dev/null +++ b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyYubikeyCodeController.js @@ -0,0 +1,59 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import MfaSetupYubikeyEntity from "../../model/entity/mfa/mfaSetupYubikeyEntity"; +import MultiFactorAuthenticationModel from "../../model/multiFactorAuthentication/multiFactorAuthenticationModel"; + + +class MfaSetupVerifyYubikeyCodeController { + /** + * MfaSetupVerifyYubikeyCodeController constructor + * @param {Worker} worker + * @param {string} requestId uuid + */ + constructor(worker, requestId, apiClientOptions) { + this.worker = worker; + this.requestId = requestId; + this.multiFactorAuthenticationModel = new MultiFactorAuthenticationModel(apiClientOptions); + } + + /** + * Controller executor. + * @param {string} provider the provider + * @returns {Promise} + */ + async _exec(providerDto) { + try { + const response = await this.exec(providerDto); + this.worker.port.emit(this.requestId, "SUCCESS", response); + } catch (error) { + console.error(error); + this.worker.port.emit(this.requestId, 'ERROR', error); + } + } + + /** + * Check and save the yubikey otp code + * @param {Object} yubikeyCode code + * @returns {Promise} + * @throws {Error} if the provider is missing + * @throws {TypeError} if the provider is not part of the enum + */ + async exec(yubikeyCode) { + const yubikeyEntity = new MfaSetupYubikeyEntity(yubikeyCode); + return await this.multiFactorAuthenticationModel.setupYubikey(yubikeyEntity); + } +} + +export default MfaSetupVerifyYubikeyCodeController; diff --git a/src/all/background_page/controller/mfaSetup/MfaSetupVerifyYubikeyCodeController.test.js b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyYubikeyCodeController.test.js new file mode 100644 index 00000000..02cf627d --- /dev/null +++ b/src/all/background_page/controller/mfaSetup/MfaSetupVerifyYubikeyCodeController.test.js @@ -0,0 +1,56 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import {enableFetchMocks} from "jest-fetch-mock"; +import {defaultApiClientOptions} from "../../service/api/apiClient/apiClientOptions.test.data"; +import EntityValidationError from "passbolt-styleguide/src/shared/models/entity/abstract/entityValidationError"; +import {mockApiResponse} from "../../../../../test/mocks/mockApiResponse"; +import MfaSetupVerifyYubikeyCodeController from "./MfaSetupVerifyYubikeyCodeController"; +import {defaultSetupYubikeyData} from "../../model/entity/mfa/mfaSetupYubikeyEntity.test.data"; +import MfaSetupYubikeyEntity from "../../model/entity/mfa/mfaSetupYubikeyEntity"; + +beforeEach(() => { + enableFetchMocks(); +}); + + +describe("MfaSetupVerifyYubikeyCodeController", () => { + let controller; + + beforeEach(() => { + controller = new MfaSetupVerifyYubikeyCodeController(null, null, defaultApiClientOptions()); + }); + + it("Should verify the otp code", async() => { + expect.assertions(1); + jest.spyOn(controller.multiFactorAuthenticationModel, "setupYubikey"); + + fetch.doMock(() => mockApiResponse({})); + + await controller.exec(defaultSetupYubikeyData()); + + expect(controller.multiFactorAuthenticationModel.setupYubikey).toHaveBeenCalledWith(new MfaSetupYubikeyEntity(defaultSetupYubikeyData())); + }); + + it("Should validate the hotp code with entity", async() => { + expect.assertions(2); + + try { + await controller.exec({}); + } catch (error) { + expect(error).toBeInstanceOf(EntityValidationError); + expect(error.hasError('hotp', 'required')).toBeTruthy(); + } + }); +}); diff --git a/src/all/background_page/controller/passwordExpiry/deletePasswordExpirySettingsController.js b/src/all/background_page/controller/passwordExpiry/deletePasswordExpirySettingsController.js new file mode 100644 index 00000000..e5dafb48 --- /dev/null +++ b/src/all/background_page/controller/passwordExpiry/deletePasswordExpirySettingsController.js @@ -0,0 +1,54 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ +import PasswordExpirySettingsModel from "../../model/passwordExpiry/passwordExpirySettingsModel"; + +class DeletePasswordExpirySettingsController { + /** + * DeletePasswordExpirySettingsController constructor + * @param {Worker} worker + * @param {string} requestId uuid + * @param {ApiClientOptions} apiClientOptions the api client options + */ + constructor(worker, requestId, apiClientOptions) { + this.worker = worker; + this.requestId = requestId; + this.passwordExpirySettingsModel = new PasswordExpirySettingsModel(apiClientOptions); + } + + /** + * Controller executor. + * @param {string} passwordExpiryId the ID of the password expiry to delete + * @returns {Promise} + */ + async _exec(passwordExpiryId) { + try { + const settings = await this.exec(passwordExpiryId); + this.worker.port.emit(this.requestId, "SUCCESS", settings); + } catch (error) { + console.error(error); + this.worker.port.emit(this.requestId, 'ERROR', error); + } + } + + /** + * Retrieve the current password expiry settings. + * @param {string} passwordExpiryId the ID of the password expiry to delete + * @returns {Promise} + */ + async exec(passwordExpiryId) { + await this.passwordExpirySettingsModel.delete(passwordExpiryId); + } +} + +export default DeletePasswordExpirySettingsController; diff --git a/src/all/background_page/controller/passwordExpiry/deletePasswordExpirySettingsController.test.js b/src/all/background_page/controller/passwordExpiry/deletePasswordExpirySettingsController.test.js new file mode 100644 index 00000000..8d739e20 --- /dev/null +++ b/src/all/background_page/controller/passwordExpiry/deletePasswordExpirySettingsController.test.js @@ -0,0 +1,66 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import {enableFetchMocks} from "jest-fetch-mock"; +import AccountEntity from "../../model/entity/account/accountEntity"; +import BuildApiClientOptionsService from "../../service/account/buildApiClientOptionsService"; +import {defaultAccountDto} from "../../model/entity/account/accountEntity.test.data"; +import {mockApiResponse, mockApiResponseError} from "../../../../../test/mocks/mockApiResponse"; +import DeletePasswordExpirySettingsController from "./deletePasswordExpirySettingsController"; +import {v4 as uuid} from "uuid"; +import PassboltApiFetchError from "../../error/passboltApiFetchError"; + +describe("DeletePasswordExpirySettingsController", () => { + let apiClientOptions; + beforeEach(async() => { + enableFetchMocks(); + jest.resetAllMocks(); + fetch.doMockIf(/users\/csrf-token\.json/, () => mockApiResponse("csrf-token")); + + const account = new AccountEntity(defaultAccountDto()); + apiClientOptions = await BuildApiClientOptionsService.buildFromAccount(account); + }); + + it("Should delete the entity from the API given an ID", () => { + expect.assertions(1); + const passwordExpiryId = uuid(); + fetch.doMockOnceIf(new RegExp(`/password-expiry\/settings\/${passwordExpiryId}\.json`), () => mockApiResponse({})); + + const controller = new DeletePasswordExpirySettingsController(null, null, apiClientOptions); + expect(() => controller.exec(passwordExpiryId)).not.toThrow(); + }); + + it("Should throw an exception if something wrong happens on the API", async() => { + expect.assertions(1); + + const passwordExpiryId = uuid(); + const errorMessage = "Something went wrong"; + const expectedError = new PassboltApiFetchError(errorMessage); + fetch.doMockOnceIf(new RegExp(`/password-expiry\/settings\/${passwordExpiryId}\.json`), () => mockApiResponseError(500, errorMessage)); + + const controller = new DeletePasswordExpirySettingsController(null, null, apiClientOptions); + await expect(() => controller.exec(passwordExpiryId)).rejects.toThrowError(expectedError); + }); + + it("Should return the default value if something goes when requesting the API", async() => { + expect.assertions(1); + + const passwordExpiryId = uuid(); + const expectedError = new Error("Something went wrong"); + fetch.doMockOnceIf(new RegExp(`/password-expiry\/settings\/${passwordExpiryId}\.json`), async() => { throw expectedError; }); + + const controller = new DeletePasswordExpirySettingsController(null, null, apiClientOptions); + await expect(() => controller.exec(passwordExpiryId)).rejects.toThrowError(expectedError); + }); +}); diff --git a/src/all/background_page/controller/passwordExpiry/findPasswordExpirySettingsController.js b/src/all/background_page/controller/passwordExpiry/findPasswordExpirySettingsController.js new file mode 100644 index 00000000..18441d5c --- /dev/null +++ b/src/all/background_page/controller/passwordExpiry/findPasswordExpirySettingsController.js @@ -0,0 +1,52 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ +import PasswordExpirySettingsModel from "../../model/passwordExpiry/passwordExpirySettingsModel"; + +class FindPasswordExpirySettingsController { + /** + * FindPasswordExpirySettingsController constructor + * @param {Worker} worker + * @param {string} requestId uuid + * @param {ApiClientOptions} apiClientOptions the api client options + */ + constructor(worker, requestId, apiClientOptions) { + this.worker = worker; + this.requestId = requestId; + this.passwordExpirySettingsModel = new PasswordExpirySettingsModel(apiClientOptions); + } + + /** + * Controller executor. + * @returns {Promise} + */ + async _exec() { + try { + const settings = await this.exec(); + this.worker.port.emit(this.requestId, "SUCCESS", settings); + } catch (error) { + console.error(error); + this.worker.port.emit(this.requestId, 'ERROR', error); + } + } + + /** + * Retrieve the current password expiry settings. + * @returns {Promise} + */ + async exec() { + return await this.passwordExpirySettingsModel.findOrDefault(); + } +} + +export default FindPasswordExpirySettingsController; diff --git a/src/all/background_page/controller/passwordExpiry/findPasswordExpirySettingsController.test.js b/src/all/background_page/controller/passwordExpiry/findPasswordExpirySettingsController.test.js new file mode 100644 index 00000000..1d6e8080 --- /dev/null +++ b/src/all/background_page/controller/passwordExpiry/findPasswordExpirySettingsController.test.js @@ -0,0 +1,70 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import {enableFetchMocks} from "jest-fetch-mock"; +import AccountEntity from "../../model/entity/account/accountEntity"; +import BuildApiClientOptionsService from "../../service/account/buildApiClientOptionsService"; +import {defaultAccountDto} from "../../model/entity/account/accountEntity.test.data"; +import {mockApiResponse, mockApiResponseError} from "../../../../../test/mocks/mockApiResponse"; +import FindPasswordExpirySettingsController from "./findPasswordExpirySettingsController"; +import {overridenPasswordExpirySettingsDto} from "passbolt-styleguide/src/shared/models/entity/passwordExpiry/passwordExpirySettingsEntity.test.data"; +import PasswordExpirySettingsEntity from "passbolt-styleguide/src/shared/models/entity/passwordExpiry/passwordExpirySettingsEntity"; + +describe("FindPasswordExpirySettingsController", () => { + let apiClientOptions; + beforeEach(async() => { + enableFetchMocks(); + jest.resetAllMocks(); + fetch.doMockIf(/users\/csrf-token\.json/, () => mockApiResponse("csrf-token")); + + const account = new AccountEntity(defaultAccountDto()); + apiClientOptions = await BuildApiClientOptionsService.buildFromAccount(account); + }); + + it("Should return the value from the API", async() => { + expect.assertions(1); + + const expectedDto = overridenPasswordExpirySettingsDto({ + default_expiry_period: 365 + }); + const expectedEntity = new PasswordExpirySettingsEntity(expectedDto); + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => mockApiResponse(expectedDto)); + + const controller = new FindPasswordExpirySettingsController(null, null, apiClientOptions); + const result = await controller.exec(); + expect(result).toStrictEqual(expectedEntity); + }); + + it("Should return the default value if the plugin is disabled", async() => { + expect.assertions(1); + + const expectedEntity = PasswordExpirySettingsEntity.createFromDefault(); + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => mockApiResponseError(500, "Something went wrong")); + + const controller = new FindPasswordExpirySettingsController(null, null, apiClientOptions); + const result = await controller.exec(); + expect(result).toStrictEqual(expectedEntity); + }); + + it("Should return the default value if something goes wrong on the API", async() => { + expect.assertions(1); + + const expectedEntity = PasswordExpirySettingsEntity.createFromDefault(); + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => { throw new Error("Something went wrong"); }); + + const controller = new FindPasswordExpirySettingsController(null, null, apiClientOptions); + const result = await controller.exec(); + expect(result).toStrictEqual(expectedEntity); + }); +}); diff --git a/src/all/background_page/controller/passwordExpiry/savePasswordExpirySettingsController.js b/src/all/background_page/controller/passwordExpiry/savePasswordExpirySettingsController.js new file mode 100644 index 00000000..725e771d --- /dev/null +++ b/src/all/background_page/controller/passwordExpiry/savePasswordExpirySettingsController.js @@ -0,0 +1,56 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ +import PasswordExpirySettingsEntity from "passbolt-styleguide/src/shared/models/entity/passwordExpiry/passwordExpirySettingsEntity"; +import PasswordExpirySettingsModel from "../../model/passwordExpiry/passwordExpirySettingsModel"; + +class SavePasswordExpirySettingsController { + /** + * SavePasswordExpirySettingsController constructor + * @param {Worker} worker + * @param {string} requestId uuid + * @param {ApiClientOptions} apiClientOptions the api client options + */ + constructor(worker, requestId, apiClientOptions) { + this.worker = worker; + this.requestId = requestId; + this.passwordExpirySettingsModel = new PasswordExpirySettingsModel(apiClientOptions); + } + + /** + * Controller executor. + * @param {PasswordExpirySettingsDto} passwordExpirySettingsDto the data to save on the API + * @returns {Promise} + */ + async _exec(passwordExpirySettingsDto) { + try { + const settings = await this.exec(passwordExpirySettingsDto); + this.worker.port.emit(this.requestId, "SUCCESS", settings); + } catch (error) { + console.error(error); + this.worker.port.emit(this.requestId, 'ERROR', error); + } + } + + /** + * Save the given user passphrase policies on the API. + * @param {PasswordExpirySettingsDto} passwordExpirySettingsDto the data to save on the API + * @returns {Promise} + */ + async exec(passwordExpirySettingsDto) { + const entity = new PasswordExpirySettingsEntity(passwordExpirySettingsDto); + return await this.passwordExpirySettingsModel.save(entity); + } +} + +export default SavePasswordExpirySettingsController; diff --git a/src/all/background_page/controller/passwordExpiry/savePasswordExpirySettingsController.test.js b/src/all/background_page/controller/passwordExpiry/savePasswordExpirySettingsController.test.js new file mode 100644 index 00000000..ef6807f6 --- /dev/null +++ b/src/all/background_page/controller/passwordExpiry/savePasswordExpirySettingsController.test.js @@ -0,0 +1,75 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import {enableFetchMocks} from "jest-fetch-mock"; +import AccountEntity from "../../model/entity/account/accountEntity"; +import BuildApiClientOptionsService from "../../service/account/buildApiClientOptionsService"; +import {defaultAccountDto} from "../../model/entity/account/accountEntity.test.data"; +import {mockApiResponse, mockApiResponseError} from "../../../../../test/mocks/mockApiResponse"; +import PassboltApiFetchError from "../../error/passboltApiFetchError"; +import PassboltServiceUnavailableError from "../../error/passboltServiceUnavailableError"; +import SavePasswordExpirySettingsController from "./savePasswordExpirySettingsController"; +import PasswordExpirySettingsEntity from "passbolt-styleguide/src/shared/models/entity/passwordExpiry/passwordExpirySettingsEntity"; +import {defaultPasswordExpirySettingsDto, defaultPasswordExpirySettingsDtoFromApi} from "passbolt-styleguide/src/shared/models/entity/passwordExpiry/passwordExpirySettingsEntity.test.data"; + +describe("SaveUserPassphrasePoliciesController", () => { + let apiClientOptions; + beforeEach(async() => { + enableFetchMocks(); + jest.resetAllMocks(); + fetch.doMockIf(/users\/csrf-token\.json/, () => mockApiResponse("csrf-token")); + + const account = new AccountEntity(defaultAccountDto()); + apiClientOptions = await BuildApiClientOptionsService.buildFromAccount(account); + }); + + it("Should save the given dto on the API", async() => { + expect.assertions(2); + + const dtoToSave = defaultPasswordExpirySettingsDto({ + default_expiry_period: 200, + }); + const expectedDto = defaultPasswordExpirySettingsDtoFromApi(dtoToSave); + const expectedEntity = new PasswordExpirySettingsEntity(expectedDto); + + fetch.doMockOnceIf(/password-expiry\/settings\.json/, async request => { + const body = JSON.parse(await request.text()); + expect(body).toStrictEqual(dtoToSave); + return mockApiResponse(expectedDto); + }); + + const controller = new SavePasswordExpirySettingsController(null, null, apiClientOptions); + const result = await controller.exec(dtoToSave); + expect(result).toStrictEqual(expectedEntity); + }); + + it("Should throw an exception if something wrong happens on the API", async() => { + expect.assertions(1); + + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => mockApiResponseError(500, "Something went wrong")); + + const dto = defaultPasswordExpirySettingsDto(); + const controller = new SavePasswordExpirySettingsController(null, null, apiClientOptions); + expect(() => controller.exec(dto)).rejects.toBeInstanceOf(PassboltApiFetchError); + }); + + it("Should return the default value if something goes when requesting the API", async() => { + expect.assertions(1); + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => { throw new Error("Something went wrong"); }); + + const dto = defaultPasswordExpirySettingsDto(); + const controller = new SavePasswordExpirySettingsController(null, null, apiClientOptions); + expect(() => controller.exec(dto)).rejects.toBeInstanceOf(PassboltServiceUnavailableError); + }); +}); diff --git a/src/all/background_page/controller/secret/secretDecryptController.test.js b/src/all/background_page/controller/secret/secretDecryptController.test.js index eab0212f..fe8a286f 100644 --- a/src/all/background_page/controller/secret/secretDecryptController.test.js +++ b/src/all/background_page/controller/secret/secretDecryptController.test.js @@ -148,7 +148,6 @@ describe("SecretDecryptController", () => { const resourceId = uuidv4(); const plaintextSecretDto = plaintextSecretTotpDto(); const encryptedSecretData = await EncryptMessageService.encrypt(JSON.stringify(plaintextSecretDto), await OpenpgpAssertion.readKeyOrFail(pgpKeys.admin.public)); - console.log(encryptedSecretData); const secretDto = readSecret({ user_id: account.userId, resource_id: resourceId, diff --git a/src/all/background_page/controller/user/updateUserController.js b/src/all/background_page/controller/user/updateUserController.js new file mode 100644 index 00000000..396a9662 --- /dev/null +++ b/src/all/background_page/controller/user/updateUserController.js @@ -0,0 +1,57 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import UserEntity from "../../model/entity/user/userEntity"; +import UserModel from "../../model/user/userModel"; + +class UpdateUserController { + /** + * Constructor + * @param {Worker} worker + * @param {string} requestId uuid + * @param {ApiClientOptions} apiClientOptions The + * @param {AccountEntity} account The account associated to the worker. + */ + constructor(worker, requestId, apiClientOptions, account) { + this.worker = worker; + this.requestId = requestId; + this.userModel = new UserModel(apiClientOptions, account); + } + + /** + * Controller executor. + * @returns {Promise} + */ + async _exec(userDto) { + try { + const updatedUser = await this.exec(userDto); + this.worker.port.emit(this.requestId, 'SUCCESS', updatedUser); + } catch (error) { + console.error(error); + this.worker.port.emit(this.requestId, 'ERROR', error); + } + } + + /** + * Update the given user on the API. + * @param {object} userDto the user dto to register on the API + * @return {Promise} + */ + async exec(userDto) { + const userEntity = new UserEntity(userDto); + return await this.userModel.update(userEntity, true); + } +} + +export default UpdateUserController; diff --git a/src/all/background_page/controller/user/updateUserController.test.js b/src/all/background_page/controller/user/updateUserController.test.js new file mode 100644 index 00000000..70e8d8aa --- /dev/null +++ b/src/all/background_page/controller/user/updateUserController.test.js @@ -0,0 +1,88 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import {enableFetchMocks} from "jest-fetch-mock"; +import {mockApiResponse, mockApiResponseError} from "../../../../../test/mocks/mockApiResponse"; +import {defaultApiClientOptions} from "../../service/api/apiClient/apiClientOptions.test.data"; +import RoleEntity from "passbolt-styleguide/src/shared/models/entity/role/roleEntity"; +import AccountEntity from "../../model/entity/account/accountEntity"; +import {defaultAccountDto} from "../../model/entity/account/accountEntity.test.data"; +import UpdateUserController from "./updateUserController"; +import {defaultUserDto} from "passbolt-styleguide/src/shared/models/entity/user/userEntity.test.data"; +import EntityValidationError from "passbolt-styleguide/src/shared/models/entity/abstract/entityValidationError"; +import PassboltApiFetchError from "../../error/passboltApiFetchError"; +import UserEntity from "../../model/entity/user/userEntity"; + +beforeEach(() => { + enableFetchMocks(); +}); + +describe("UpdateUserController", () => { + describe("::exec", () => { + const accountDto = defaultAccountDto(); + accountDto.role_name = RoleEntity.ROLE_ADMIN; + const account = new AccountEntity(accountDto); + + it("Should update the user given the DTO", async() => { + expect.assertions(2); + + const expectedDto = defaultUserDto({ + "disabled": new Date().toISOString().split('.')[0], + }); + + const expectedRequestDto = JSON.parse(JSON.stringify(expectedDto)); + delete expectedRequestDto.role; + delete expectedRequestDto.profile.avatar; + + fetch.doMockOnceIf(new RegExp(`/users/${expectedDto.id}.json`), async req => { + const body = JSON.parse(await req.text()); + expect(body).toStrictEqual(expectedRequestDto); + return mockApiResponse(expectedDto); + }); + + const controller = new UpdateUserController(null, null, defaultApiClientOptions(), account); + const resultingEntity = await controller.exec(expectedDto); + expect(resultingEntity).toStrictEqual(new UserEntity(expectedDto)); + }); + + it("Should throw an error if the user entity cannot validate", async() => { + expect.assertions(1); + + const wrongDto = defaultUserDto({ + "disabled": true, + }); + + const controller = new UpdateUserController(null, null, defaultApiClientOptions(), account); + try { + await controller.exec(wrongDto); + } catch (e) { + expect(e).toBeInstanceOf(EntityValidationError); + } + }); + + it("Should throw an error if something wrong happens on the API", async() => { + expect.assertions(1); + + const dto = defaultUserDto(); + fetch.doMockOnceIf(new RegExp(`/users/${dto.id}.json`), async() => mockApiResponseError(500, "Something went wrong")); + + const controller = new UpdateUserController(null, null, defaultApiClientOptions(), account); + try { + await controller.exec(dto); + } catch (e) { + expect(e).toBeInstanceOf(PassboltApiFetchError); + } + }); + }); +}); diff --git a/src/all/background_page/event/appEvents.js b/src/all/background_page/event/appEvents.js index 0965e9f2..4e50c775 100644 --- a/src/all/background_page/event/appEvents.js +++ b/src/all/background_page/event/appEvents.js @@ -40,8 +40,11 @@ import ExportDesktopAccountController from "../controller/exportAccount/exportDe import GetLegacyAccountService from "../service/account/getLegacyAccountService"; import FindUserPassphrasePoliciesController from "../controller/userPassphrasePolicies/findUserPassphrasePoliciesController"; import SaveUserPassphrasePoliciesController from "../controller/userPassphrasePolicies/saveUserPassphrasePoliciesController"; +import SavePasswordExpirySettingsController from "../controller/passwordExpiry/savePasswordExpirySettingsController"; +import DeletePasswordExpirySettingsController from "../controller/passwordExpiry/deletePasswordExpirySettingsController"; +import FindPasswordExpirySettingsController from "../controller/passwordExpiry/findPasswordExpirySettingsController"; -const listen = function(worker, _, account) { +const listen = function(worker, apiClientOptions, account) { const authenticationEventController = new AuthenticationEventController(worker); authenticationEventController.startListen(); /* @@ -227,6 +230,28 @@ const listen = function(worker, _, account) { await controller._exec(userPassphrasePoliciesDto); }); + /* + * ================================================================================== + * Password expiry settings events. + * ================================================================================== + */ + + worker.port.on('passbolt.password-expiry.find', async requestId => { + const controller = new FindPasswordExpirySettingsController(worker, requestId, apiClientOptions); + await controller._exec(); + }); + + worker.port.on('passbolt.password-expiry.save', async(requestId, passwordExpirySettingsDto) => { + const controller = new SavePasswordExpirySettingsController(worker, requestId, apiClientOptions); + await controller._exec(passwordExpirySettingsDto); + }); + + worker.port.on('passbolt.password-expiry.delete', async(requestId, passwordExpiryId) => { + const controller = new DeletePasswordExpirySettingsController(worker, requestId, apiClientOptions); + await controller._exec(passwordExpiryId); + }); + + /* * ================================================================================== * Desktop app events. diff --git a/src/all/background_page/event/authEvents.js b/src/all/background_page/event/authEvents.js index a2db71b1..d136e8e4 100644 --- a/src/all/background_page/event/authEvents.js +++ b/src/all/background_page/event/authEvents.js @@ -217,6 +217,15 @@ const listen = function(worker, _, account) { await signInWithSso(requestId, isInQuickaccessMode, SsoSettingsEntity.GOOGLE); }); + /** + * Attempt to sign in with a generic OAuth2 as a third party sign in provider + * @listens passbolt.sso.sign-in-with-oauth2 + * @param {uuid} requestId The request identifier + */ + worker.port.on('passbolt.sso.sign-in-with-oauth2', async(requestId, isInQuickaccessMode) => { + await signInWithSso(requestId, isInQuickaccessMode, SsoSettingsEntity.OAUTH2); + }); + /** * Returns the sso provider id registered client-side. * @listens passbolt.sso.get-local-configured-provider diff --git a/src/all/background_page/event/mfaEvents.js b/src/all/background_page/event/mfaEvents.js index 9efdffed..16bd6c63 100644 --- a/src/all/background_page/event/mfaEvents.js +++ b/src/all/background_page/event/mfaEvents.js @@ -17,6 +17,10 @@ import MfaGetPolicyController from '../controller/mfaPolicy/mfaGetPolicyControll import User from "../model/user"; import MfaGetMfaSettingsController from '../controller/mfaPolicy/mfaGetMfaSettingsController'; import HasUserPostponedUserSettingInvitationMFAPolicyController from '../controller/mfaPolicy/hasUserPostponedUserSettingInvitationController'; +import MfaSetupVerifyTotpCodeController from '../controller/mfaSetup/MfaSetupVerifyTotpCodeController'; +import MfaSetupVerifyProviderController from '../controller/mfaSetup/MfaSetupVerifyProviderController'; +import MfaSetupRemoveProviderController from '../controller/mfaSetup/MfaSetupRemoveProviderController'; +import MfaSetupVerifyYubikeyCodeController from '../controller/mfaSetup/MfaSetupVerifyYubikeyCodeController'; /** * Listens to the account recovery continue application events @@ -45,6 +49,30 @@ const listen = function(worker) { const controller = new MfaGetMfaSettingsController(worker, requestId, apiClientOptions); await controller._exec(); }); + + worker.port.on('passbolt.mfa-setup.verify-provider', async(requestId, providerDto) => { + const apiClientOptions = await User.getInstance().getApiClientOptions(); + const controller = new MfaSetupVerifyProviderController(worker, requestId, apiClientOptions); + await controller._exec(providerDto); + }); + + worker.port.on('passbolt.mfa-setup.verify-totp-code', async(requestId, code) => { + const apiClientOptions = await User.getInstance().getApiClientOptions(); + const controller = new MfaSetupVerifyTotpCodeController(worker, requestId, apiClientOptions); + await controller._exec(code); + }); + + worker.port.on('passbolt.mfa-setup.verify-yubikey-code', async(requestId, code) => { + const apiClientOptions = await User.getInstance().getApiClientOptions(); + const controller = new MfaSetupVerifyYubikeyCodeController(worker, requestId, apiClientOptions); + await controller._exec(code); + }); + + worker.port.on('passbolt.mfa-setup.remove-provider', async(requestId, providerDto) => { + const apiClientOptions = await User.getInstance().getApiClientOptions(); + const controller = new MfaSetupRemoveProviderController(worker, requestId, apiClientOptions); + await controller._exec(providerDto); + }); }; export const MfaEvents = {listen}; diff --git a/src/all/background_page/event/userEvents.js b/src/all/background_page/event/userEvents.js index 593bcff4..062bf8fc 100644 --- a/src/all/background_page/event/userEvents.js +++ b/src/all/background_page/event/userEvents.js @@ -16,7 +16,7 @@ import SecurityTokenEntity from "../model/entity/securityToken/securityTokenEnti import AvatarUpdateEntity from "../model/entity/avatar/update/avatarUpdateEntity"; import UpdateUserLocalStorageController from "../controller/user/updateUserLocalStorageController"; import GetOrFindLoggedInUserController from "../controller/user/getOrFindLoggedInUserController"; - +import UpdateUserController from "../controller/user/updateUserController"; const listen = function(worker, _, account) { /* @@ -83,22 +83,15 @@ const listen = function(worker, _, account) { * Update a user * Can be used to change the role or firstname/lastname but nothing else * - * @listens passbolt.user.update + * @listens passbolt.users.update * @param requestId {uuid} The request identifier * @param userDato {Object} The user object, example: * {id: , username: 'ada@passbolt.com', profile: {first_name: 'ada', last_name: 'lovelace'}, role_id: } */ worker.port.on('passbolt.users.update', async(requestId, userDto) => { - try { - const clientOptions = await User.getInstance().getApiClientOptions(); - const userModel = new UserModel(clientOptions, account); - const userEntity = new UserEntity(userDto); - const updatedUser = await userModel.update(userEntity, true); - worker.port.emit(requestId, 'SUCCESS', updatedUser); - } catch (error) { - console.error(error); - worker.port.emit(requestId, 'ERROR', error); - } + const clientOptions = await User.getInstance().getApiClientOptions(); + const controller = new UpdateUserController(worker, requestId, clientOptions, account); + await controller._exec(userDto); }); /* diff --git a/src/all/background_page/model/entity/mfa/mfaProviderEntity.js b/src/all/background_page/model/entity/mfa/mfaProviderEntity.js new file mode 100644 index 00000000..342ffae1 --- /dev/null +++ b/src/all/background_page/model/entity/mfa/mfaProviderEntity.js @@ -0,0 +1,89 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import Entity from "passbolt-styleguide/src/shared/models/entity/abstract/entity"; +import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/entitySchema"; + + +const ENTITY_NAME = 'MfaProviderEntity'; +const YUBIKEY = "yubikey"; +const TOTP = "totp"; +const DUO = "duo"; + +class MfaProviderEntity extends Entity { + /** + * Mfa provider constructor + * + * @param {Object} provider mfa provider + * @throws {EntityValidationError} if the dto cannot be converted into an entity + */ + constructor(providerDto) { + super(EntitySchema.validate( + MfaProviderEntity.ENTITY_NAME, + providerDto, + MfaProviderEntity.getSchema() + )); + } + + /** + * Get mfa provider entity schema + * @returns {Object} schema + */ + static getSchema() { + return { + "type": "object", + "required": [ + "provider", + ], + "properties": { + "provider": { + "type": "string", + "enum": [ + YUBIKEY, + TOTP, + DUO + ] + }, + } + }; + } + /* + * ================================================== + * Dynamic properties getters + * ================================================== + */ + + /** + * Get the mfa provider + * @returns {string} + */ + get provider() { + return this._props.provider; + } + + /* + * ================================================== + * Static properties getters + * ================================================== + */ + /** + * MfaPolicyEntity.ENTITY_NAME + * @returns {string} + */ + static get ENTITY_NAME() { + return ENTITY_NAME; + } +} + +export default MfaProviderEntity; diff --git a/src/all/background_page/model/entity/mfa/mfaProviderEntity.test.data.js b/src/all/background_page/model/entity/mfa/mfaProviderEntity.test.data.js new file mode 100644 index 00000000..42c094e3 --- /dev/null +++ b/src/all/background_page/model/entity/mfa/mfaProviderEntity.test.data.js @@ -0,0 +1,21 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +export const defaultMfaProviderData = (props = {}) => { + const data = { + provider: "totp" + }; + return Object.assign(data, props); +}; + diff --git a/src/all/background_page/model/entity/mfa/mfaProviderEntity.test.js b/src/all/background_page/model/entity/mfa/mfaProviderEntity.test.js new file mode 100644 index 00000000..5c338f6e --- /dev/null +++ b/src/all/background_page/model/entity/mfa/mfaProviderEntity.test.js @@ -0,0 +1,49 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/entitySchema"; +import EntityValidationError from "passbolt-styleguide/src/shared/models/entity/abstract/entityValidationError"; +import MfaProviderEntity from "./mfaProviderEntity"; +import {defaultMfaProviderData} from "./mfaProviderEntity.test.data"; + +describe("MfaProviderEntity", () => { + it("schema must validate", () => { + EntitySchema.validateSchema(MfaProviderEntity.ENTITY_NAME, MfaProviderEntity.getSchema()); + }); + + it("constructor works if valid minimal DTO is provided", () => { + const entity = new MfaProviderEntity(defaultMfaProviderData()); + + expect(entity._props).toEqual(defaultMfaProviderData()); + }); + + it("constructor returns validation error if provider field is missing", () => { + try { + new MfaProviderEntity({}); + } catch (error) { + expect(error instanceof EntityValidationError).toBe(true); + expect(error.hasError('provider', 'required')).toBe(true); + } + }); + it("constructor returns validation error if provider field is not part of the enum", () => { + try { + new MfaProviderEntity(defaultMfaProviderData({ + provider: "google" + })); + } catch (error) { + expect(error instanceof EntityValidationError).toBe(true); + expect(error.hasError('provider', 'enum')).toBe(true); + } + }); +}); diff --git a/src/all/background_page/model/entity/mfa/mfaSetupTotpEntity.js b/src/all/background_page/model/entity/mfa/mfaSetupTotpEntity.js new file mode 100644 index 00000000..c1bb9ec4 --- /dev/null +++ b/src/all/background_page/model/entity/mfa/mfaSetupTotpEntity.js @@ -0,0 +1,73 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import Entity from "passbolt-styleguide/src/shared/models/entity/abstract/entity"; +import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/entitySchema"; + + +const ENTITY_NAME = 'MfaSetupTotp'; + +class MfaSetupTotpEntity extends Entity { + /** + * Mfa policy entity constructor + * + * @param {Object} setupDto totp setup dto + * @throws {EntityValidationError} if the dto cannot be converted into an entity + */ + constructor(setupDto) { + super(EntitySchema.validate( + MfaSetupTotpEntity.ENTITY_NAME, + setupDto, + MfaSetupTotpEntity.getSchema() + )); + } + + /** + * Get mfa policy entity schema + * @returns {Object} schema + */ + static getSchema() { + return { + "type": "object", + "required": [ + "otpProvisioningUri", + "totp" + ], + "properties": { + "totp": { + "type": "string", + "pattern": /^[a-z0-9]{6}$/ + }, + "otpProvisioningUri": { + "type": "string", + } + } + }; + } + + /* + * ================================================== + * Static properties getters + * ================================================== + */ + /** + * MfaPolicyEntity.ENTITY_NAME + * @returns {string} + */ + static get ENTITY_NAME() { + return ENTITY_NAME; + } +} + +export default MfaSetupTotpEntity; diff --git a/src/all/background_page/model/entity/mfa/mfaSetupTotpEntity.test.data.js b/src/all/background_page/model/entity/mfa/mfaSetupTotpEntity.test.data.js new file mode 100644 index 00000000..80c304bb --- /dev/null +++ b/src/all/background_page/model/entity/mfa/mfaSetupTotpEntity.test.data.js @@ -0,0 +1,22 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +export const defaultSetupTotpData = (props = {}) => { + const data = { + otpProvisioningUri: "otpauth://totp/www.passbolt.local:admin%40passbolt.com?issuer=www.passbolt.local&secret=TVWEGQFS3WPCID6GYAPHHCC54VXHFUL7EC5FVHEMVH7CKQI2XEQQ&algorithm=SHA1&digits=6&period=30", + totp: "663516" + }; + return Object.assign(data, props); +}; + diff --git a/src/all/background_page/model/entity/mfa/mfaSetupTotpEntity.test.js b/src/all/background_page/model/entity/mfa/mfaSetupTotpEntity.test.js new file mode 100644 index 00000000..67bcafd8 --- /dev/null +++ b/src/all/background_page/model/entity/mfa/mfaSetupTotpEntity.test.js @@ -0,0 +1,51 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/entitySchema"; +import EntityValidationError from "passbolt-styleguide/src/shared/models/entity/abstract/entityValidationError"; +import {defaultVerifyProviderData} from "./mfaVerifyProviderEntity.test.data"; +import MfaSetupTotpEntity from "./mfaSetupTotpEntity"; +import {defaultSetupTotpData} from "./mfaSetupTotpEntity.test.data"; + +describe("MfaSetupTotpEntity", () => { + it("schema must validate", () => { + EntitySchema.validateSchema(MfaSetupTotpEntity.ENTITY_NAME, MfaSetupTotpEntity.getSchema()); + }); + + it("constructor works if valid minimal DTO is provided", () => { + const entity = new MfaSetupTotpEntity(defaultSetupTotpData()); + + expect(entity._props).toEqual(defaultSetupTotpData()); + }); + + it("constructor returns validation error if verified field is missing", () => { + try { + new MfaSetupTotpEntity({}); + } catch (error) { + expect(error instanceof EntityValidationError).toBe(true); + expect(error.hasError('totp', 'required')).toBe(true); + expect(error.hasError('otpProvisioningUri', 'required')).toBe(true); + } + }); + it("constructor returns validation error if totp code is not a valid format", () => { + try { + new MfaSetupTotpEntity(defaultVerifyProviderData({ + totp: "221" + })); + } catch (error) { + expect(error instanceof EntityValidationError).toBe(true); + expect(error.hasError('totp', 'pattern')).toBe(true); + } + }); +}); diff --git a/src/all/background_page/model/entity/mfa/mfaSetupYubikeyEntity.js b/src/all/background_page/model/entity/mfa/mfaSetupYubikeyEntity.js new file mode 100644 index 00000000..fd6374a9 --- /dev/null +++ b/src/all/background_page/model/entity/mfa/mfaSetupYubikeyEntity.js @@ -0,0 +1,69 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import Entity from "passbolt-styleguide/src/shared/models/entity/abstract/entity"; +import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/entitySchema"; + + +const ENTITY_NAME = 'MfaSetupYubikeyEntity'; + +class MfaSetupYubikeyEntity extends Entity { + /** + * Mfa setup yubikey entity provider constructor + * + * @param {Object} setupDto yubikey setup dto + * @throws {EntityValidationError} if the dto cannot be converted into an entity + */ + constructor(setupDto) { + super(EntitySchema.validate( + MfaSetupYubikeyEntity.ENTITY_NAME, + setupDto, + MfaSetupYubikeyEntity.getSchema() + )); + } + + /** + * Get mfa policy entity schema + * @returns {Object} schema + */ + static getSchema() { + return { + "type": "object", + "required": [ + "hotp", + ], + "properties": { + "hotp": { + "type": "string", + "pattern": /^[cbdefghijklnrtuv]{44}$/ + }, + } + }; + } + + /* + * ================================================== + * Static properties getters + * ================================================== + */ + /** + * MfaPolicyEntity.ENTITY_NAME + * @returns {string} + */ + static get ENTITY_NAME() { + return ENTITY_NAME; + } +} + +export default MfaSetupYubikeyEntity; diff --git a/src/all/background_page/model/entity/mfa/mfaSetupYubikeyEntity.test.data.js b/src/all/background_page/model/entity/mfa/mfaSetupYubikeyEntity.test.data.js new file mode 100644 index 00000000..c52075ae --- /dev/null +++ b/src/all/background_page/model/entity/mfa/mfaSetupYubikeyEntity.test.data.js @@ -0,0 +1,21 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +export const defaultSetupYubikeyData = (props = {}) => { + const data = { + hotp: "cccccbenrnifbhrkgctignjbbrrvidhcjdhkciigeuce", + }; + return Object.assign(data, props); +}; + diff --git a/src/all/background_page/model/entity/mfa/mfaSetupYubikeyEntity.test.js b/src/all/background_page/model/entity/mfa/mfaSetupYubikeyEntity.test.js new file mode 100644 index 00000000..33e05208 --- /dev/null +++ b/src/all/background_page/model/entity/mfa/mfaSetupYubikeyEntity.test.js @@ -0,0 +1,54 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/entitySchema"; +import EntityValidationError from "passbolt-styleguide/src/shared/models/entity/abstract/entityValidationError"; +import MfaSetupYubikeyEntity from "./mfaSetupYubikeyEntity"; +import {defaultSetupYubikeyData} from "./mfaSetupYubikeyEntity.test.data"; + +describe("MfaSetupYubikeyEntity", () => { + it("schema must validate", () => { + EntitySchema.validateSchema(MfaSetupYubikeyEntity.ENTITY_NAME, MfaSetupYubikeyEntity.getSchema()); + }); + + it("constructor works if valid minimal DTO is provided", () => { + expect.assertions(1); + const entity = new MfaSetupYubikeyEntity(defaultSetupYubikeyData()); + + expect(entity._props).toEqual(defaultSetupYubikeyData()); + }); + + it("constructor returns validation error if verified field is missing", () => { + expect.assertions(2); + + try { + new MfaSetupYubikeyEntity({}); + } catch (error) { + expect(error instanceof EntityValidationError).toBe(true); + expect(error.hasError('hotp', 'required')).toBe(true); + } + }); + it("constructor returns validation error if hotp code is not a valid format", () => { + expect.assertions(2); + + try { + new MfaSetupYubikeyEntity(defaultSetupYubikeyData({ + hotp: "221" + })); + } catch (error) { + expect(error instanceof EntityValidationError).toBe(true); + expect(error.hasError('hotp', 'pattern')).toBe(true); + } + }); +}); diff --git a/src/all/background_page/model/entity/mfa/mfaVerifyProviderEntity.js b/src/all/background_page/model/entity/mfa/mfaVerifyProviderEntity.js new file mode 100644 index 00000000..6370d075 --- /dev/null +++ b/src/all/background_page/model/entity/mfa/mfaVerifyProviderEntity.js @@ -0,0 +1,69 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import Entity from "passbolt-styleguide/src/shared/models/entity/abstract/entity"; +import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/entitySchema"; + + +const ENTITY_NAME = 'MfaVerifyProvider'; + +class MfaVerifyProviderEntity extends Entity { + /** + * Mfa verify provider constructor + * + * @param {Object} setupDto totp setup dto + * @throws {EntityValidationError} if the dto cannot be converted into an entity + */ + constructor(setupDto) { + super(EntitySchema.validate( + MfaVerifyProviderEntity.ENTITY_NAME, + setupDto, + MfaVerifyProviderEntity.getSchema() + )); + } + + /** + * Get mfa policy entity schema + * @returns {Object} schema + */ + static getSchema() { + return { + "type": "object", + "required": [ + "verified", + ], + "properties": { + "verified": { + "type": "string", + "format": "date-time" + }, + } + }; + } + + /* + * ================================================== + * Static properties getters + * ================================================== + */ + /** + * MfaPolicyEntity.ENTITY_NAME + * @returns {string} + */ + static get ENTITY_NAME() { + return ENTITY_NAME; + } +} + +export default MfaVerifyProviderEntity; diff --git a/src/all/background_page/model/entity/mfa/mfaVerifyProviderEntity.test.data.js b/src/all/background_page/model/entity/mfa/mfaVerifyProviderEntity.test.data.js new file mode 100644 index 00000000..8c01b044 --- /dev/null +++ b/src/all/background_page/model/entity/mfa/mfaVerifyProviderEntity.test.data.js @@ -0,0 +1,21 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +export const defaultVerifyProviderData = (props = {}) => { + const data = { + verified: "2023-09-27T13:03:23+00:00" + }; + return Object.assign(data, props); +}; + diff --git a/src/all/background_page/model/entity/mfa/mfaVerifyProviderEntity.test.js b/src/all/background_page/model/entity/mfa/mfaVerifyProviderEntity.test.js new file mode 100644 index 00000000..0b96ec29 --- /dev/null +++ b/src/all/background_page/model/entity/mfa/mfaVerifyProviderEntity.test.js @@ -0,0 +1,49 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/entitySchema"; +import EntityValidationError from "passbolt-styleguide/src/shared/models/entity/abstract/entityValidationError"; +import MfaVerifyProviderEntity from "./mfaVerifyProviderEntity"; +import {defaultVerifyProviderData} from "./mfaVerifyProviderEntity.test.data"; + +describe("MfaVerifyProviderEntity", () => { + it("schema must validate", () => { + EntitySchema.validateSchema(MfaVerifyProviderEntity.ENTITY_NAME, MfaVerifyProviderEntity.getSchema()); + }); + + it("constructor works if valid minimal DTO is provided", () => { + const entity = new MfaVerifyProviderEntity(defaultVerifyProviderData()); + + expect(entity._props).toEqual(defaultVerifyProviderData()); + }); + + it("constructor returns validation error if verified field is missing", () => { + try { + new MfaVerifyProviderEntity({}); + } catch (error) { + expect(error instanceof EntityValidationError).toBe(true); + expect(error.hasError('verified', 'required')).toBe(true); + } + }); + it("constructor returns validation error if verified field is not a string", () => { + try { + new MfaVerifyProviderEntity(defaultVerifyProviderData({ + verified: 3 + })); + } catch (error) { + expect(error instanceof EntityValidationError).toBe(true); + expect(error.hasError('verified', 'type')).toBe(true); + } + }); +}); diff --git a/src/all/background_page/model/entity/passwordPolicies/passwordPoliciesEntity.js b/src/all/background_page/model/entity/passwordPolicies/passwordPoliciesEntity.js index 8ec65fcf..e115cbe2 100644 --- a/src/all/background_page/model/entity/passwordPolicies/passwordPoliciesEntity.js +++ b/src/all/background_page/model/entity/passwordPolicies/passwordPoliciesEntity.js @@ -77,6 +77,9 @@ class PasswordPoliciesEntity extends Entity { }, "password_generator_settings": PasswordGeneratorSettingsEntity.getSchema(), "passphrase_generator_settings": PassphraseGeneratorSettingsEntity.getSchema(), + "source": { + "type": "string", + }, "created": { "type": "string", "format": "date-time" diff --git a/src/all/background_page/model/entity/resource/resourceEntity.js b/src/all/background_page/model/entity/resource/resourceEntity.js index 3ea876e5..b80b6bd9 100644 --- a/src/all/background_page/model/entity/resource/resourceEntity.js +++ b/src/all/background_page/model/entity/resource/resourceEntity.js @@ -112,6 +112,14 @@ class ResourceEntity extends Entity { "type": "null" }] }, + "expired": { + "anyOf": [{ + "type": "string", + "format": "date-time" + }, { + "type": "null" + }] + }, "deleted": { "type": "boolean" }, diff --git a/src/all/background_page/model/entity/resource/resourcesCollection.js b/src/all/background_page/model/entity/resource/resourcesCollection.js index 8d8c20f3..17bc0713 100644 --- a/src/all/background_page/model/entity/resource/resourcesCollection.js +++ b/src/all/background_page/model/entity/resource/resourcesCollection.js @@ -17,6 +17,7 @@ import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/ import EntityCollectionError from "passbolt-styleguide/src/shared/models/entity/abstract/entityCollectionError"; import deduplicateObjects from "../../../utils/array/deduplicateObjects"; import canSuggestUrl from "../../../utils/url/canSuggestUrl"; +import resourcesCollection from "./resourcesCollection"; const ENTITY_NAME = 'Resources'; const RULE_UNIQUE_ID = 'unique_id'; @@ -124,7 +125,7 @@ class ResourcesCollection extends EntityCollection { /** * Find the possible resources to suggest given an url * @param url An url - * @return {*[]} A list of resource + * @return {ResourcesCollection} A list of resource */ findSuggestedResources(url) { const suggestedResources = []; @@ -139,7 +140,7 @@ class ResourcesCollection extends EntityCollection { } } } - return suggestedResources; + return new resourcesCollection(suggestedResources); } /** diff --git a/src/all/background_page/model/entity/resourceType/resourceTypesCollection.js b/src/all/background_page/model/entity/resourceType/resourceTypesCollection.js index 7543ce81..580bfaa7 100644 --- a/src/all/background_page/model/entity/resourceType/resourceTypesCollection.js +++ b/src/all/background_page/model/entity/resourceType/resourceTypesCollection.js @@ -126,6 +126,20 @@ class ResourceTypesCollection extends EntityCollection { return this.resourceTypes.some(resourceType => resourceType.id === id); } + /** + * Is resource type password present + * @param id The id + * @return {boolean} + */ + isPasswordResourceType(id) { + const passwordResourceTypesSlug = [ + RESOURCE_TYPE_PASSWORD_STRING_SLUG, + RESOURCE_TYPE_PASSWORD_AND_DESCRIPTION_SLUG, + RESOURCE_TYPE_PASSWORD_DESCRIPTION_TOTP_SLUG, + ]; + return this.resourceTypes.some(resourceType => resourceType.id === id && passwordResourceTypesSlug.includes(resourceType.slug)); + } + /* * ================================================== * Setters diff --git a/src/all/background_page/model/entity/sso/ssoLoginUrlEntity.js b/src/all/background_page/model/entity/sso/ssoLoginUrlEntity.js index 59bff145..38c80a1d 100644 --- a/src/all/background_page/model/entity/sso/ssoLoginUrlEntity.js +++ b/src/all/background_page/model/entity/sso/ssoLoginUrlEntity.js @@ -13,6 +13,7 @@ */ import Entity from "passbolt-styleguide/src/shared/models/entity/abstract/entity"; import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/entitySchema"; +import SsoSettingsEntity from "./ssoSettingsEntity"; const ENTITY_NAME = "SsoLoginUrl"; const SSO_LOGIN_SUPPORTED_URLS = { @@ -81,6 +82,13 @@ class SsoLoginUrlEntity extends Entity { throw new Error('The url should be a valid url.'); } + if (ssoProvider === SsoSettingsEntity.OAUTH2) { + if (url.protocol !== "https:") { + throw new Error('The url protocol should be HTTPS.'); + } + return; + } + if (!SSO_LOGIN_SUPPORTED_URLS[ssoProvider]) { throw new Error('The url should be part of the list of supported single sign-on urls.'); } diff --git a/src/all/background_page/model/entity/sso/ssoLoginUrlEntity.test.js b/src/all/background_page/model/entity/sso/ssoLoginUrlEntity.test.js index b4bb10e0..b6f81b92 100644 --- a/src/all/background_page/model/entity/sso/ssoLoginUrlEntity.test.js +++ b/src/all/background_page/model/entity/sso/ssoLoginUrlEntity.test.js @@ -26,7 +26,8 @@ describe("Sso Login URL Entity", () => { {providerId: "azure", url: 'https://login.microsoftonline.com'}, {providerId: "azure", url: 'https://login.microsoftonline.us'}, {providerId: "azure", url: 'https://login.partner.microsoftonline.cn'}, - {providerId: "google", url: 'https://accounts.google.com'} + {providerId: "google", url: 'https://accounts.google.com'}, + {providerId: "oauth2", url: 'https://oauth2.generic.provider.com'}, ]; expect.assertions(availableUrl.length); @@ -67,6 +68,7 @@ describe("Sso Login URL Entity", () => { {scenario: 'Query parameter attack', url: 'https://attacker.com?domain=https://login.microsoftonline.com'}, {scenario: 'Hash attack', url: 'https://attacker.com#https://login.microsoftonline.com'}, {scenario: 'Mixing provider URL attack', url: 'https://login.microsoftonline.com', providerId: "google"}, + {scenario: 'Not using HTTPS', url: 'http://not.secure.com', providerId: "oauth2"}, ]).describe("Should not accept unsupported or attacker url", test => { it(`Should not accept unsupported or attacker url: ${test.scenario}`, async() => { const dto = { diff --git a/src/all/background_page/model/entity/sso/ssoSettingsEntity.js b/src/all/background_page/model/entity/sso/ssoSettingsEntity.js index 8a85efbb..b14343ee 100644 --- a/src/all/background_page/model/entity/sso/ssoSettingsEntity.js +++ b/src/all/background_page/model/entity/sso/ssoSettingsEntity.js @@ -17,6 +17,7 @@ import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/ const ENTITY_NAME = "SsoSettings"; const AZURE = "azure"; const GOOGLE = "google"; +const OAUTH2 = "oauth2"; const DATE_REGEXP = /^\d{4}-\d{2}-\d{2}$/; const DATETIME_REGEXP = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/; @@ -185,6 +186,14 @@ class SsoSettingsEntity extends Entity { return GOOGLE; } + /** + * SsoSettingsEntity.GOOGLE + * @returns {string} + */ + static get OAUTH2() { + return OAUTH2; + } + /** * SsoSettingsEntity.AVAILABLE_PROVIDERS * @returns {Array} @@ -193,6 +202,7 @@ class SsoSettingsEntity extends Entity { return [ SsoSettingsEntity.AZURE, SsoSettingsEntity.GOOGLE, + SsoSettingsEntity.OAUTH2, ]; } } diff --git a/src/all/background_page/model/entity/user/userEntity.js b/src/all/background_page/model/entity/user/userEntity.js index ad69ecc6..c6ff96f6 100644 --- a/src/all/background_page/model/entity/user/userEntity.js +++ b/src/all/background_page/model/entity/user/userEntity.js @@ -95,6 +95,14 @@ class UserEntity extends Entity { "deleted": { "type": "boolean" }, + "disabled": { + "anyOf": [{ + "type": "string", + "format": "date-time" + }, { + "type": "null" + }] + }, "created": { "type": "string", "format": "date-time" diff --git a/src/all/background_page/model/entity/user/userEntity.test.fixtures.js b/src/all/background_page/model/entity/user/userEntity.test.fixtures.js index 3dbeb11f..3e416783 100644 --- a/src/all/background_page/model/entity/user/userEntity.test.fixtures.js +++ b/src/all/background_page/model/entity/user/userEntity.test.fixtures.js @@ -14,13 +14,14 @@ import {pgpKeys} from "../../../../../../test/fixtures/pgpKeys/keys"; -exports.UserEntityTestFixtures = { - "default": { +export const defaultUserEntityTestFixtures = (data = {}) => { + const defaultData = { "id": "d57c10f5-639d-5160-9c81-8a0c6c4ec856", "role_id": "0d51c3a8-5e67-5e3d-882f-e1868966d817", "username": "admin@passbolt.com", "active": true, "deleted": false, + "disabled": null, "created": "2020-04-20T11:32:16+00:00", "modified": "2020-04-20T11:32:16+00:00", "last_logged_in": "2012-07-04T13:39:25+00:00", @@ -94,5 +95,6 @@ exports.UserEntityTestFixtures = { "created_by": "d57c10f5-639d-5160-9c81-8a0c6c4ec856", "modified_by": "d57c10f5-639d-5160-9c81-8a0c6c4ec856" } - } + }; + return Object.assign(defaultData, data); }; diff --git a/src/all/background_page/model/entity/user/userEntity.test.js b/src/all/background_page/model/entity/user/userEntity.test.js index 97d080cb..2342d47b 100644 --- a/src/all/background_page/model/entity/user/userEntity.test.js +++ b/src/all/background_page/model/entity/user/userEntity.test.js @@ -12,7 +12,7 @@ * @since 2.13.0 */ import UserEntity from "./userEntity"; -import {UserEntityTestFixtures} from "./userEntity.test.fixtures"; +import {defaultUserEntityTestFixtures} from "./userEntity.test.fixtures"; import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/entitySchema"; import EntityValidationError from "passbolt-styleguide/src/shared/models/entity/abstract/entityValidationError"; import { @@ -42,13 +42,16 @@ describe("User entity", () => { }); it("constructor works if valid DTO with associated entity data is provided", () => { - const dto = UserEntityTestFixtures.default; + const dto = defaultUserEntityTestFixtures({ + "disabled": "2025-04-20T11:32:16+00:00", + }); const filtered = { "id": "d57c10f5-639d-5160-9c81-8a0c6c4ec856", "role_id": "0d51c3a8-5e67-5e3d-882f-e1868966d817", "username": "admin@passbolt.com", "active": true, "deleted": false, + "disabled": "2025-04-20T11:32:16+00:00", "created": "2020-04-20T11:32:16+00:00", "modified": "2020-04-20T11:32:16+00:00", "last_logged_in": "2012-07-04T13:39:25+00:00", @@ -90,18 +93,21 @@ describe("User entity", () => { }); it("constructor throws an exception if DTO contains invalid field", () => { + expect.assertions(5); + try { new UserEntity({ "id": "🤷", "role_id": -0, "username": "(ノಠ益ಠ)ノ", + "disabled": "it's not a date for sure", }); - expect(false).toBe(true); } catch (error) { - expect((error instanceof EntityValidationError)).toBe(true); + expect(error).toBeInstanceOf(EntityValidationError); expect(error.hasError('id', 'format')).toBe(true); expect(error.hasError('role_id', 'type')).toBe(true); expect(error.hasError('username', 'custom')).toBe(true); + expect(error.hasError('disabled', 'type')).toBe(true); } }); @@ -126,7 +132,7 @@ describe("User entity", () => { }); it("serialization works with full object inside collection", () => { - const dto = UserEntityTestFixtures.default; + const dto = defaultUserEntityTestFixtures(); const entity = new UserEntity(dto); expect(entity.groupsUsers).not.toBeNull(); expect(entity.groupsUsers.items[0].id).toEqual('03e26ff8-81d2-5b7f-87e4-99bbc40e1f95'); diff --git a/src/all/background_page/model/multiFactorAuthentication/multiFactorAuthenticationModel.js b/src/all/background_page/model/multiFactorAuthentication/multiFactorAuthenticationModel.js index ee2a0416..e1cf346b 100644 --- a/src/all/background_page/model/multiFactorAuthentication/multiFactorAuthenticationModel.js +++ b/src/all/background_page/model/multiFactorAuthentication/multiFactorAuthenticationModel.js @@ -16,6 +16,7 @@ import UserEntity from "../entity/user/userEntity"; import MultiFactorAuthenticationPolicyService from '../../service/api/multiFactorAuthentication/multiFactorAuthenticationPolicyService'; import MfaPolicyEntity from '../entity/mfa/mfaPolicyEntity'; import MfaCombinedEnabledProvidersEntity from '../entity/mfa/mfaCombinedEnabledProvidersEntity'; +import MfaVerifyProviderEntity from "../entity/mfa/mfaVerifyProviderEntity"; class MultiFactorAuthenticationModel { /** @@ -67,6 +68,47 @@ class MultiFactorAuthenticationModel { const setting = await this.multiFactorAuthenticationService.getSettings(); return new MfaCombinedEnabledProvidersEntity(setting); } + + /** + * Setup the topt provider + * @param {MfaSetupTotpEntity} totpEntity + * @returns {Promise} + * @public + */ + async setupTotp(totpEntity) { + await this.multiFactorAuthenticationService.setupTotp(totpEntity.toDto()); + } + + /** + * Setup the yubikey provider + * @param {MfaSetupYubikeyEntity} totpEntity + * @returns {Promise} + * @public + */ + async setupYubikey(totpEntity) { + await this.multiFactorAuthenticationService.setupYubikey(totpEntity.toDto()); + } + + /** + * Verify the provider setup + * @param {MfaProviderEntity} providerEntity + * @returns {Promise} + * @public + */ + async verifyProvider(providerEntity) { + const result = await this.multiFactorAuthenticationService.verifyProvider(providerEntity.provider); + return new MfaVerifyProviderEntity(result); + } + + /** + * Remove the provider from configuration + * @param {MfaProviderEntity} providerEntity + * @returns {Promise} + * @public + */ + async removeProvider(providerEntity) { + await this.multiFactorAuthenticationService.removeProvider(providerEntity.provider); + } } export default MultiFactorAuthenticationModel; diff --git a/src/all/background_page/model/multiFactorAuthentication/multiFactorAuthenticationModel.test.js b/src/all/background_page/model/multiFactorAuthentication/multiFactorAuthenticationModel.test.js new file mode 100644 index 00000000..76c6e118 --- /dev/null +++ b/src/all/background_page/model/multiFactorAuthentication/multiFactorAuthenticationModel.test.js @@ -0,0 +1,71 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import {enableFetchMocks} from "jest-fetch-mock"; +import {mockApiResponse} from "../../../../../test/mocks/mockApiResponse"; +import {defaultApiClientOptions} from "../../service/api/apiClient/apiClientOptions.test.data"; +import MultiFactorAuthenticationModel from "./multiFactorAuthenticationModel"; +import {defaultSetupTotpData} from "../entity/mfa/mfaSetupTotpEntity.test.data"; +import {defaultVerifyProviderData} from "../entity/mfa/mfaVerifyProviderEntity.test.data"; +import MfaVerifyProviderEntity from "../entity/mfa/mfaVerifyProviderEntity"; +import {defaultMfaProviderData} from "../entity/mfa/mfaProviderEntity.test.data"; +import MfaSetupTotpEntity from "../entity/mfa/mfaSetupTotpEntity"; +import MfaProviderEntity from "../entity/mfa/mfaProviderEntity"; + +beforeEach(() => { + enableFetchMocks(); +}); + + +describe("MultiFactorAuthenticationModel", () => { + let model; + + beforeEach(() => { + model = new MultiFactorAuthenticationModel(defaultApiClientOptions()); + }); + + it("Should able to setup totp", async() => { + expect.assertions(1); + jest.spyOn(model.multiFactorAuthenticationService, "setupTotp"); + + fetch.doMock(() => mockApiResponse({})); + + await model.setupTotp(new MfaSetupTotpEntity(defaultSetupTotpData())); + + expect(model.multiFactorAuthenticationService.setupTotp).toHaveBeenCalledWith(defaultSetupTotpData()); + }); + + it("Should able to verify a MFA configuration", async() => { + expect.assertions(2); + jest.spyOn(model.multiFactorAuthenticationService, "verifyProvider"); + + fetch.doMock(() => mockApiResponse(defaultVerifyProviderData())); + + const result = await model.verifyProvider(new defaultMfaProviderData()); + + expect(model.multiFactorAuthenticationService.verifyProvider).toHaveBeenCalledWith("totp"); + expect(result).toEqual(new MfaVerifyProviderEntity(defaultVerifyProviderData())); + }); + + it("Should able to remove a MFA provider", async() => { + expect.assertions(1); + jest.spyOn(model.multiFactorAuthenticationService, "removeProvider"); + + fetch.doMock(() => mockApiResponse({})); + + await model.removeProvider(new MfaProviderEntity(defaultMfaProviderData())); + + expect(model.multiFactorAuthenticationService.removeProvider).toHaveBeenCalledWith("totp"); + }); +}); diff --git a/src/all/background_page/model/passwordExpiry/passwordExpirySettingsModel.js b/src/all/background_page/model/passwordExpiry/passwordExpirySettingsModel.js new file mode 100644 index 00000000..1bf0b372 --- /dev/null +++ b/src/all/background_page/model/passwordExpiry/passwordExpirySettingsModel.js @@ -0,0 +1,67 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import PasswordExpirySettingsService from "../../service/api/passwordExpiry/passwordExpirySettingsService"; +import {assertType, assertUuid} from "../../utils/assertions"; +import PasswordExpirySettingsEntity from "passbolt-styleguide/src/shared/models/entity/passwordExpiry/passwordExpirySettingsEntity"; + +class PasswordExpirySettingsModel { + /** + * Constructor + * + * @param {ApiClientOptions} apiClientOptions + * @public + */ + constructor(apiClientOptions) { + this.passwordExpirySettingsService = new PasswordExpirySettingsService(apiClientOptions); + } + + /** + * Find the current password expiry settings from the API. + * @returns {Promise} + */ + async findOrDefault() { + let passwordExpirySettingsDto = null; + try { + passwordExpirySettingsDto = await this.passwordExpirySettingsService.find(); + return PasswordExpirySettingsEntity.createFromDefault(passwordExpirySettingsDto); + } catch (error) { + console.error(error); + } + return PasswordExpirySettingsEntity.createFromDefault(); + } + + /** + * Saves the password expiry settings on the API. + * @param {PasswordExpirySettingsEntity} passwordExpirySettingsEntity the entity to register + * @returns {Promise} + */ + async save(passwordExpirySettingsEntity) { + assertType(passwordExpirySettingsEntity, PasswordExpirySettingsEntity, 'The given entity is not a PasswordExpirySettingsEntity'); + const passwordExpirySettingsDto = await this.passwordExpirySettingsService.create(passwordExpirySettingsEntity); + return PasswordExpirySettingsEntity.createFromDefault(passwordExpirySettingsDto); + } + + /** + * Delete the password expiry settings on the API given an ID. + * @param {string} passwordExpirySettingsId the ID of the entity to delete + * @returns {Promise} + */ + async delete(passwordExpirySettingsId) { + assertUuid(passwordExpirySettingsId, "The password expiry settings id should be a valid uuid."); + await this.passwordExpirySettingsService.delete(passwordExpirySettingsId); + } +} + +export default PasswordExpirySettingsModel; diff --git a/src/all/background_page/model/passwordExpiry/passwordExpirySettingsModel.test.js b/src/all/background_page/model/passwordExpiry/passwordExpirySettingsModel.test.js new file mode 100644 index 00000000..021ec556 --- /dev/null +++ b/src/all/background_page/model/passwordExpiry/passwordExpirySettingsModel.test.js @@ -0,0 +1,182 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import {enableFetchMocks} from "jest-fetch-mock"; +import AccountEntity from "../entity/account/accountEntity"; +import BuildApiClientOptionsService from "../../service/account/buildApiClientOptionsService"; +import {defaultAccountDto} from "../entity/account/accountEntity.test.data"; +import {mockApiResponse, mockApiResponseError} from "../../../../../test/mocks/mockApiResponse"; +import PassboltApiFetchError from "../../error/passboltApiFetchError"; +import PassboltServiceUnavailableError from "../../error/passboltServiceUnavailableError"; +import PasswordPoliciesEntity from "../entity/passwordPolicies/passwordPoliciesEntity"; +import {defaultPasswordExpirySettingsDto, defaultPasswordExpirySettingsDtoFromApi} from "passbolt-styleguide/src/shared/models/entity/passwordExpiry/passwordExpirySettingsEntity.test.data"; +import PasswordExpirySettingsEntity from "passbolt-styleguide/src/shared/models/entity/passwordExpiry/passwordExpirySettingsEntity"; +import PasswordExpirySettingsModel from "./passwordExpirySettingsModel"; +import {v4 as uuid} from "uuid"; +import PassboltBadResponseError from "../../error/passboltBadResponseError"; + +describe("PasswordExpiry model", () => { + let apiClientOptions; + beforeEach(async() => { + enableFetchMocks(); + jest.resetAllMocks(); + fetch.doMockIf(/users\/csrf-token\.json/, () => mockApiResponse("csrf-token")); + + const account = new AccountEntity(defaultAccountDto()); + apiClientOptions = await BuildApiClientOptionsService.buildFromAccount(account); + }); + + describe('::findOrDefault', () => { + it("should return the value stored on the API", async() => { + expect.assertions(1); + const expectedDto = defaultPasswordExpirySettingsDtoFromApi(); + const expectedEntity = new PasswordExpirySettingsEntity(expectedDto); + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => mockApiResponse(expectedDto)); + + const model = new PasswordExpirySettingsModel(apiClientOptions); + const result = await model.findOrDefault(); + + expect(result).toStrictEqual(expectedEntity); + }); + + it("should return a default entity if the API returns an HTTP error", async() => { + expect.assertions(1); + const expectedEntity = PasswordExpirySettingsEntity.createFromDefault(); + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => mockApiResponseError(404, "Endpoint is not existing")); + + const model = new PasswordExpirySettingsModel(apiClientOptions); + const result = await model.findOrDefault(); + + expect(result).toStrictEqual(expectedEntity); + }); + + it("should return a default entity if something goes wrong on the API", async() => { + expect.assertions(1); + const expectedEntity = PasswordExpirySettingsEntity.createFromDefault(); + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => { throw new Error("Something went wrong"); }); + + const model = new PasswordExpirySettingsModel(apiClientOptions); + const result = await model.findOrDefault(); + + expect(result).toStrictEqual(expectedEntity); + }); + }); + + describe('::save', () => { + it("should save the entity and return the stored value from the API", async() => { + expect.assertions(2); + const baseData = { + default_expiry_period: 30, + policy_override: true, + automatic_update: false, + expiry_notification: 5, + }; + const dtoToSave = defaultPasswordExpirySettingsDto(baseData); + const entityToSave = new PasswordExpirySettingsEntity(dtoToSave); + + const expectedDto = defaultPasswordExpirySettingsDtoFromApi(baseData); + const expectedEntity = new PasswordExpirySettingsEntity(expectedDto); + + fetch.doMockOnceIf(/password-expiry\/settings\.json/, async request => { + const body = JSON.parse(await request.text()); + expect(body).toStrictEqual(dtoToSave); + return mockApiResponse(expectedDto); + }); + + const model = new PasswordExpirySettingsModel(apiClientOptions); + const result = await model.save(entityToSave); + + expect(result).toStrictEqual(expectedEntity); + }); + + it("should throw an Error if the data to save is invalid", async() => { + expect.assertions(1); + + const model = new PasswordExpirySettingsModel(apiClientOptions); + const entity = PasswordPoliciesEntity.createFromDefault(); + const expectedError = new Error('The given entity is not a PasswordExpirySettingsEntity'); + expect(() => model.save(entity)).rejects.toThrow(expectedError); + }); + + it("should throw an Error if something goes wrong on the API", async() => { + expect.assertions(1); + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => mockApiResponseError(500, "Endpoint is not existing")); + + const model = new PasswordExpirySettingsModel(apiClientOptions); + const entity = new PasswordExpirySettingsEntity(defaultPasswordExpirySettingsDto()); + try { + await model.save(entity); + } catch (e) { + expect(e).toBeInstanceOf(PassboltApiFetchError); + } + }); + + it("should throw an Error if the API returns an HTTP error", async() => { + expect.assertions(1); + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => { throw new Error("Something went wrong"); }); + + const model = new PasswordExpirySettingsModel(apiClientOptions); + const entity = new PasswordExpirySettingsEntity(defaultPasswordExpirySettingsDto()); + try { + await model.save(entity); + } catch (e) { + expect(e).toBeInstanceOf(PassboltServiceUnavailableError); + } + }); + }); + + describe('::delete', () => { + it("should delete the entity from the API given an ID", async() => { + const passwordExpiryId = uuid(); + fetch.doMockOnceIf(new RegExp(`/password-expiry\/settings\/${passwordExpiryId}\.json`), () => mockApiResponse({})); + + const model = new PasswordExpirySettingsModel(apiClientOptions); + await expect(model.delete(passwordExpiryId)).resolves.not.toThrow(); + }); + + it("should throw an Error if the ID to delete is not a UUID", async() => { + expect.assertions(1); + + const model = new PasswordExpirySettingsModel(apiClientOptions); + const expectedError = new Error("The password expiry settings id should be a valid uuid."); + expect(() => model.delete("test")).rejects.toThrow(expectedError); + }); + + it("should throw an Error if something goes wrong on the API", async() => { + expect.assertions(1); + const passwordExpiryId = uuid(); + fetch.doMockOnceIf(new RegExp(`/password-expiry\/settings\/${passwordExpiryId}\.json`), mockApiResponseError(500, "Something wrong happened")); + + const model = new PasswordExpirySettingsModel(apiClientOptions); + try { + await model.delete(passwordExpiryId); + } catch (e) { + expect(e).toBeInstanceOf(PassboltBadResponseError); + } + }); + + it("should throw an Error if something goes wrong when request the API", async() => { + expect.assertions(1); + const passwordExpiryId = uuid(); + fetch.doMockOnceIf(new RegExp(`/password-expiry\/settings\/${passwordExpiryId}\.json`), () => { throw new Error("Something went wrong"); }); + + const model = new PasswordExpirySettingsModel(apiClientOptions); + try { + await model.delete(passwordExpiryId); + } catch (e) { + expect(e).toBeInstanceOf(PassboltServiceUnavailableError); + } + }); + }); +}); diff --git a/src/all/background_page/model/resource/resourceModel.js b/src/all/background_page/model/resource/resourceModel.js index 37cc4bc5..5abaa56b 100644 --- a/src/all/background_page/model/resource/resourceModel.js +++ b/src/all/background_page/model/resource/resourceModel.js @@ -98,7 +98,7 @@ class ResourceModel { /** * Returns the cached collection of resoures or fetch them otherwise - * @return {Promise} + * @return {Promise} */ async getOrFindAll() { const localResources = await ResourceLocalStorage.get(); @@ -272,14 +272,17 @@ class ResourceModel { /** * Returns the count of possible resources to suggest given an url - * @param currentUrl An url + * @param {string} url An url * @return {*[]|number} */ async countSuggestedResources(url) { if (!url) { return 0; } - return (await this.getOrFindAll()).countSuggestedResources(url); + const resourcesCollection = await this.getOrFindAll(); + const passwordResources = await this.keepPasswordResources(resourcesCollection.toDto()); + const passwordResourcesCollection = new ResourcesCollection(passwordResources); + return passwordResourcesCollection.countSuggestedResources(url); } /** @@ -291,8 +294,10 @@ class ResourceModel { if (!url) { return 0; } - const toDto = resourceEntity => resourceEntity.toDto(); - return (await this.getOrFindAll()).findSuggestedResources(url).map(toDto); + const resourcesCollection = await this.getOrFindAll(); + const passwordResources = await this.keepPasswordResources(resourcesCollection.toDto()); + const passwordResourcesCollection = new ResourcesCollection(passwordResources); + return passwordResourcesCollection.findSuggestedResources(url).toDto(); } /* @@ -627,6 +632,16 @@ class ResourceModel { const resourceTypesCollection = await this.resourceTypeModel.getOrFindAll(); return resourcesDto.filter(resource => !resource.resource_type_id || resourceTypesCollection.isResourceTypeIdPresent(resource.resource_type_id)); } + + /** + * Keep only resources supported with no or known resource type + * @param resourcesDto + * @return {Promise<*[]>} + */ + async keepPasswordResources(resourcesDto) { + const resourceTypesCollection = await this.resourceTypeModel.getOrFindAll(); + return resourcesDto.filter(resource => !resource.resource_type_id || resourceTypesCollection.isPasswordResourceType(resource.resource_type_id)); + } } export default ResourceModel; diff --git a/src/all/background_page/model/userPassphrasePolicies/userPassphrasePoliciesModel.test.js b/src/all/background_page/model/userPassphrasePolicies/userPassphrasePoliciesModel.test.js index dcb089fc..d961a7f2 100644 --- a/src/all/background_page/model/userPassphrasePolicies/userPassphrasePoliciesModel.test.js +++ b/src/all/background_page/model/userPassphrasePolicies/userPassphrasePoliciesModel.test.js @@ -50,7 +50,7 @@ describe("UserPassphrasePolicies model", () => { expect(result).toStrictEqual(expectedEntity); }); - it("should return a default entity if something goes wrong on the API", async() => { + it("should return a default entity if the API returns an HTTP error", async() => { expect.assertions(1); const expectedEntity = UserPassphrasePoliciesEntity.createFromDefault(); fetch.doMockOnceIf(/user-passphrase-policies\/settings\.json/, () => mockApiResponseError(404, "Endpoint is not existing")); @@ -74,7 +74,7 @@ describe("UserPassphrasePolicies model", () => { }); describe('::save', () => { - it("should save a user passphrase policies and return the stored value from the API", async() => { + it("should save the entity and return the stored value from the API", async() => { expect.assertions(2); const baseData = { entropy_minimum: 112, @@ -107,7 +107,7 @@ describe("UserPassphrasePolicies model", () => { expect(() => model.save(entity)).rejects.toThrow(expectedError); }); - it("should throw an Error if something goes wrong on the API", async() => { + it("should throw an Error if the API returns an HTTP error", async() => { expect.assertions(1); fetch.doMockOnceIf(/user-passphrase-policies\/settings\.json/, () => mockApiResponseError(500, "Endpoint is not existing")); diff --git a/src/all/background_page/pagemod/quickAccessPagemod.js b/src/all/background_page/pagemod/quickAccessPagemod.js index ab02fd61..1a4444c9 100644 --- a/src/all/background_page/pagemod/quickAccessPagemod.js +++ b/src/all/background_page/pagemod/quickAccessPagemod.js @@ -26,6 +26,7 @@ import {LocaleEvents} from "../event/localeEvents"; import {PownedPasswordEvents} from '../event/pownedPasswordEvents'; import GetLegacyAccountService from "../service/account/getLegacyAccountService"; import {RememberMeEvents} from "../event/rememberMeEvents"; +import {ResourceTypeEvents} from "../event/resourceTypeEvents"; class QuickAccess extends Pagemod { /** @@ -53,7 +54,8 @@ class QuickAccess extends Pagemod { TabEvents, LocaleEvents, PownedPasswordEvents, - RememberMeEvents + RememberMeEvents, + ResourceTypeEvents ]; } diff --git a/src/all/background_page/pagemod/quickAccessPagemod.test.js b/src/all/background_page/pagemod/quickAccessPagemod.test.js index 290e1cb5..71fc969c 100644 --- a/src/all/background_page/pagemod/quickAccessPagemod.test.js +++ b/src/all/background_page/pagemod/quickAccessPagemod.test.js @@ -28,6 +28,7 @@ import GetLegacyAccountService from "../service/account/getLegacyAccountService" import {v4 as uuid} from 'uuid'; import {enableFetchMocks} from "jest-fetch-mock"; import {RememberMeEvents} from "../event/rememberMeEvents"; +import {ResourceTypeEvents} from "../event/resourceTypeEvents"; jest.spyOn(GetLegacyAccountService, "get").mockImplementation(jest.fn()); jest.spyOn(AuthEvents, "listen").mockImplementation(jest.fn()); @@ -43,6 +44,7 @@ jest.spyOn(TabEvents, "listen").mockImplementation(jest.fn()); jest.spyOn(LocaleEvents, "listen").mockImplementation(jest.fn()); jest.spyOn(PownedPasswordEvents, "listen").mockImplementation(jest.fn()); jest.spyOn(RememberMeEvents, "listen").mockImplementation(jest.fn()); +jest.spyOn(ResourceTypeEvents, "listen").mockImplementation(jest.fn()); describe("QuickAccess", () => { beforeEach(async() => { @@ -53,7 +55,7 @@ describe("QuickAccess", () => { describe("QuickAccess::attachEvents", () => { it("Should attach events", async() => { - expect.assertions(16); + expect.assertions(17); // data mocked const port = { _port: { @@ -80,7 +82,8 @@ describe("QuickAccess", () => { expect(LocaleEvents.listen).toHaveBeenCalledWith(expectedArgument, apiClientOptions, mockedAccount); expect(PownedPasswordEvents.listen).toHaveBeenCalledWith(expectedArgument, apiClientOptions, mockedAccount); expect(RememberMeEvents.listen).toHaveBeenCalledWith(expectedArgument, apiClientOptions, mockedAccount); - expect(QuickAccess.events).toStrictEqual([AuthEvents, ConfigEvents, KeyringEvents, QuickAccessEvents, GroupEvents, TagEvents, ResourceEvents, SecretEvents, OrganizationSettingsEvents, TabEvents, LocaleEvents, PownedPasswordEvents, RememberMeEvents]); + expect(ResourceTypeEvents.listen).toHaveBeenCalledWith(expectedArgument, apiClientOptions, mockedAccount); + expect(QuickAccess.events).toStrictEqual([AuthEvents, ConfigEvents, KeyringEvents, QuickAccessEvents, GroupEvents, TagEvents, ResourceEvents, SecretEvents, OrganizationSettingsEvents, TabEvents, LocaleEvents, PownedPasswordEvents, RememberMeEvents, ResourceTypeEvents]); expect(QuickAccess.mustReloadOnExtensionUpdate).toBeFalsy(); expect(QuickAccess.appName).toBe('QuickAccess'); }); diff --git a/src/all/background_page/service/api/multiFactorAuthentication/multiFactorAuthenticationService.js b/src/all/background_page/service/api/multiFactorAuthentication/multiFactorAuthenticationService.js index 428c568e..90779ec5 100644 --- a/src/all/background_page/service/api/multiFactorAuthentication/multiFactorAuthenticationService.js +++ b/src/all/background_page/service/api/multiFactorAuthentication/multiFactorAuthenticationService.js @@ -63,6 +63,59 @@ class MultiFactorAuthenticationService extends AbstractService { const settings = await this.apiClient.fetchAndHandleResponse('GET', url); return settings.body; } + + /** + * setup the totp provider + * @param {Object} body + * @throw {TypeError} invalid otp provisioning uri + * @throw {TypeError} invalid otp code + * @public + */ + async setupTotp(body) { + const bodyString = this.apiClient.buildBody(body); + const url = this.apiClient.buildUrl(`${this.apiClient.baseUrl}/setup/totp`); + await this.apiClient.fetchAndHandleResponse('POST', url, bodyString); + } + + /** + * setup the yubikey provider + * @param {Object} body + * @returns {Promise} + * @throw {TypeError} invalid otp provisioning uri + * @throw {TypeError} invalid otp code + * @public + */ + async setupYubikey(body) { + const bodyString = this.apiClient.buildBody(body); + const url = this.apiClient.buildUrl(`${this.apiClient.baseUrl}/setup/yubikey`); + await this.apiClient.fetchAndHandleResponse('POST', url, bodyString); + } + + + /** + * Verify the provider + * @param {string} provider + * @throw {TypeError} invalid provider + * @public + */ + async verifyProvider(provider) { + const url = this.apiClient.buildUrl(`${this.apiClient.baseUrl}/setup/${provider}`); + const result = await this.apiClient.fetchAndHandleResponse('GET', url); + return result.body; + } + + /** + * Remove the provider + * @param {string} provider + * @throw {TypeError} invalid provider + * @public + */ + async removeProvider(provider) { + const url = this.apiClient.buildUrl(`${this.apiClient.baseUrl}/setup/${provider}`); + const result = await this.apiClient.fetchAndHandleResponse('DELETE', url); + return result.body; + } } + export default MultiFactorAuthenticationService; diff --git a/src/all/background_page/service/api/passwordExpiry/passwordExpirySettingsService.js b/src/all/background_page/service/api/passwordExpiry/passwordExpirySettingsService.js new file mode 100644 index 00000000..ad248b66 --- /dev/null +++ b/src/all/background_page/service/api/passwordExpiry/passwordExpirySettingsService.js @@ -0,0 +1,72 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ +import AbstractService from "../abstract/abstractService"; + +const PASSWORD_EXPIRY_SETTINGS_RESOURCE_NAME = 'password-expiry/settings'; + +class PasswordExpirySettingsService extends AbstractService { + /** + * Constructor + * + * @param {ApiClientOptions} apiClientOptions + * @public + */ + constructor(apiClientOptions) { + super(apiClientOptions, PasswordExpirySettingsService.RESOURCE_NAME); + } + + /** + * Get settings for password-expiry. + * @returns {Promise} Response body + * @public + */ + async find() { + const setting = await this.apiClient.findAll(); + return setting.body; + } + + /** + * Create password-expiry settings on the API. + * @param {Object} data the settings to save + * @returns {Promise} Response body + * @public + */ + async create(data) { + this.assertNonEmptyData(data); + const setting = await this.apiClient.create(data); + return setting.body; + } + + /** + * Delete the given password-expiry settings. + * @param {Object} data the settings to save + * @returns {Promise} Response body + * @public + */ + async delete(passwordExpirySettingsId) { + await this.apiClient.delete(passwordExpirySettingsId); + } + + /** + * API Resource Name + * + * @returns {string} + * @public + */ + static get RESOURCE_NAME() { + return PASSWORD_EXPIRY_SETTINGS_RESOURCE_NAME; + } +} + +export default PasswordExpirySettingsService; diff --git a/src/all/background_page/service/api/passwordExpiry/passwordExpirySettingsService.test.js b/src/all/background_page/service/api/passwordExpiry/passwordExpirySettingsService.test.js new file mode 100644 index 00000000..66b7f54d --- /dev/null +++ b/src/all/background_page/service/api/passwordExpiry/passwordExpirySettingsService.test.js @@ -0,0 +1,157 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.4.0 + */ + +import {enableFetchMocks} from "jest-fetch-mock"; +import AccountEntity from "../../../model/entity/account/accountEntity"; +import BuildApiClientOptionsService from "../../account/buildApiClientOptionsService"; +import {defaultAccountDto} from "../../../model/entity/account/accountEntity.test.data"; +import {mockApiResponse, mockApiResponseError} from '../../../../../../test/mocks/mockApiResponse'; +import PassboltApiFetchError from "../../../error/passboltApiFetchError"; +import PassboltServiceUnavailableError from "../../../error/passboltServiceUnavailableError"; +import PasswordExpirySettingsService from "./passwordExpirySettingsService"; +import {v4 as uuid} from 'uuid'; +import {defaultPasswordExpirySettingsDto, defaultPasswordExpirySettingsDtoFromApi} from "passbolt-styleguide/src/shared/models/entity/passwordExpiry/passwordExpirySettingsEntity.test.data"; + +describe("PasswordExpiry service", () => { + let apiClientOptions; + beforeEach(async() => { + enableFetchMocks(); + jest.resetAllMocks(); + fetch.doMockIf(/users\/csrf-token\.json/, () => mockApiResponse("csrf-token")); + + const account = new AccountEntity(defaultAccountDto()); + apiClientOptions = await BuildApiClientOptionsService.buildFromAccount(account); + }); + + describe('::find', () => { + it("should return the dto served by the API", async() => { + expect.assertions(1); + const expectedDto = defaultPasswordExpirySettingsDto(); + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => mockApiResponse(expectedDto)); + + const service = new PasswordExpirySettingsService(apiClientOptions); + const resultDto = await service.find(); + + expect(resultDto).toStrictEqual(expectedDto); + }); + + it("should throw an error if the API returns an error response", async() => { + expect.assertions(1); + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => mockApiResponseError(500, "Something went wrong!")); + + const service = new PasswordExpirySettingsService(apiClientOptions); + try { + await service.find(); + } catch (e) { + expect(e).toBeInstanceOf(PassboltApiFetchError); + } + }); + + it("should throw an error if something happens on the API", async() => { + expect.assertions(1); + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => { throw new Error("Something went wrong"); }); + + const service = new PasswordExpirySettingsService(apiClientOptions); + try { + await service.find(); + } catch (e) { + expect(e).toBeInstanceOf(PassboltServiceUnavailableError); + } + }); + }); + + describe('::create', () => { + it("Should create the settings on the API and return the saved data from the API", async() => { + expect.assertions(2); + const dataToRegister = defaultPasswordExpirySettingsDto(); + const expectedDto = defaultPasswordExpirySettingsDtoFromApi(dataToRegister); + + fetch.doMockOnceIf(/password-expiry\/settings\.json/, async request => { + const body = JSON.parse(await request.text()); + expect(body).toStrictEqual(dataToRegister); + return mockApiResponse(expectedDto); + }); + + const service = new PasswordExpirySettingsService(apiClientOptions); + const resultDto = await service.create(dataToRegister); + + expect(resultDto).toStrictEqual(expectedDto); + }); + + it("Should throw an error if an error happens on the API", async() => { + expect.assertions(1); + + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => mockApiResponseError(500, "Something wrong happened!")); + + const service = new PasswordExpirySettingsService(apiClientOptions); + try { + await service.create(defaultPasswordExpirySettingsDto()); + } catch (e) { + expect(e).toBeInstanceOf(PassboltApiFetchError); + } + }); + + it("Should throw an error if an error happens when requesting the API", async() => { + expect.assertions(1); + + fetch.doMockOnceIf(/password-expiry\/settings\.json/, () => { throw new Error("Something wrong happened"); }); + + const service = new PasswordExpirySettingsService(apiClientOptions); + try { + await service.create(defaultPasswordExpirySettingsDto()); + } catch (e) { + expect(e).toBeInstanceOf(PassboltServiceUnavailableError); + } + }); + }); + + describe('::delete', () => { + it("should delete the resource with the given id from the API", async() => { + expect.assertions(1); + const expectedId = uuid(); + fetch.doMockOnceIf(new RegExp(`/password-expiry\/settings\/${expectedId}\.json`), () => mockApiResponse({})); + + const service = new PasswordExpirySettingsService(apiClientOptions); + await expect(service.delete(expectedId)).resolves.not.toThrow(); + }); + + it("should throw an error if the API returns an error response", async() => { + expect.assertions(1); + + const expectedId = uuid(); + fetch.doMockOnceIf(new RegExp(`/password-expiry\/settings\/${expectedId}\.json`), () => mockApiResponseError(500, "Something went wrong!")); + + const service = new PasswordExpirySettingsService(apiClientOptions); + try { + await service.delete(expectedId); + } catch (e) { + expect(e).toBeInstanceOf(PassboltApiFetchError); + } + }); + + it("should throw an error if something happens on the API", async() => { + expect.assertions(1); + + const expectedId = uuid(); + fetch.doMockOnceIf(new RegExp(`/password-expiry\/settings\/${expectedId}\.json`), () => { throw new Error("Something went wrong"); }); + + const service = new PasswordExpirySettingsService(apiClientOptions); + try { + await service.delete(expectedId); + } catch (e) { + expect(e).toBeInstanceOf(PassboltServiceUnavailableError); + } + }); + }); +}); diff --git a/src/all/background_page/service/crypto/decryptMessageService.js b/src/all/background_page/service/crypto/decryptMessageService.js index b1aabd8f..720b4fb7 100644 --- a/src/all/background_page/service/crypto/decryptMessageService.js +++ b/src/all/background_page/service/crypto/decryptMessageService.js @@ -38,7 +38,7 @@ class DecryptMessageService { }); if (verificationKeys) { - await this._doSignatureVerification(signatures); + await this.doSignatureVerification(signatures); } return decryptedMessage; @@ -68,7 +68,7 @@ class DecryptMessageService { }); if (verificationKeys) { - await this._doSignatureVerification(signatures); + await this.doSignatureVerification(signatures); } return decryptedMessage; @@ -80,9 +80,8 @@ class DecryptMessageService { * @param {Array} signatures The signatures to run the verification for * @returns {Promise} * @throws {Error} if any of the signature does not verify the message. - * @private */ - static async _doSignatureVerification(signatures) { + static async doSignatureVerification(signatures) { /* * Openpgp.js's signatures is an array of signature to verify. * The verification process is run by starting a promise from the signature.verified field. diff --git a/src/all/background_page/service/crypto/signMessageService.js b/src/all/background_page/service/crypto/signMessageService.js new file mode 100644 index 00000000..2c676a41 --- /dev/null +++ b/src/all/background_page/service/crypto/signMessageService.js @@ -0,0 +1,38 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.3.0 + */ + +import * as openpgp from 'openpgp'; +import {OpenpgpAssertion} from "../../utils/openpgp/openpgpAssertions"; + +class SignMessageService { + /** + * Sign a text message with the private key. + * + * @param {openpgp.Message} message The message to sign. + * @param {openpgp.PrivateKey} signingKeys The private key to use to sign the message. + * @returns {Promise} + * @throws {TypeError} If the message is not a valid openpgp.Message + * @throws {TypeError} If one of the provided key is not a valid openpgp.PrivateKey + */ + static async sign(message, signingKeys) { + OpenpgpAssertion.assertMessage(message); + OpenpgpAssertion.assertDecryptedPrivateKeys(signingKeys); + + const signedMesage = await openpgp.sign({message: message, signingKeys: signingKeys}); + + return signedMesage; + } +} + +export default SignMessageService; diff --git a/src/all/background_page/service/crypto/signMessageService.test.js b/src/all/background_page/service/crypto/signMessageService.test.js new file mode 100644 index 00000000..76df3489 --- /dev/null +++ b/src/all/background_page/service/crypto/signMessageService.test.js @@ -0,0 +1,56 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.3.0 + */ + +import {pgpKeys} from '../../../../../test/fixtures/pgpKeys/keys'; +import {OpenpgpAssertion} from '../../utils/openpgp/openpgpAssertions'; +import SignMessageService from "./signMessageService"; +import VerifyMessageService from './verifyMessageSign'; +import * as openpgp from 'openpgp'; + +describe("SignMessageService service", () => { + it("should sign a given message key with a provide key provided", async() => { + expect.assertions(1); + + const messageToSign = await openpgp.createMessage({text: 'my-account-kit'}); + const adminDecryptedKey = await OpenpgpAssertion.readKeyOrFail(pgpKeys.admin.private_decrypted); + + const signedMessage = await SignMessageService.sign(messageToSign, [adminDecryptedKey]); + //verify the signature + const readSignedMessage = await openpgp.readMessage({ + armoredMessage: signedMessage // parse armored message + }); + + const verificationKeys = await OpenpgpAssertion.readAllKeysOrFail([pgpKeys.admin.public]); + + await expect(VerifyMessageService.verify(readSignedMessage, verificationKeys)).resolves.not.toThrow(); + }); + + it("should throw an error in case an invalid message", async() => { + expect.assertions(1); + + const adminDecryptedKey = await OpenpgpAssertion.readKeyOrFail(pgpKeys.admin.private_decrypted); + const promise = SignMessageService.sign("", [adminDecryptedKey]); + + return expect(promise).rejects.toThrowError(new TypeError("The message should be a valid openpgp message.")); + }); + + it("should throw an error in case an invalid private key", async() => { + expect.assertions(1); + const messageToSign = await openpgp.createMessage({text: 'my-account-kit'}); + const promise = SignMessageService.sign(messageToSign, ""); + + return expect(promise).rejects.toThrowError(new Error("The keys should be an array of valid decrypted openpgp private keys.")); + }); +}); + diff --git a/src/all/background_page/service/crypto/verifyMessageSign.js b/src/all/background_page/service/crypto/verifyMessageSign.js new file mode 100644 index 00000000..fa2baa55 --- /dev/null +++ b/src/all/background_page/service/crypto/verifyMessageSign.js @@ -0,0 +1,39 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.3.0 + */ + +import * as openpgp from 'openpgp'; +import {OpenpgpAssertion} from "../../utils/openpgp/openpgpAssertions"; +import DecryptMessageService from './decryptMessageService'; + +class VerifyMessageService { + /** + * Sign a text message with the private key. + * + * @param {openpgp.Message} message The message to sign. + * @param {array} verificationKeys (optional) The key(s) to check the signature for. + * @returns {Promise} + * @throws {TypeError} If the message is not a valid openpgp.Message + * @throws {TypeError} If one of the provided key is not a valid openpgp.PublicKey or openpgp.PrivateKey + * @throws {TypeError} If the message cannot be verified + */ + static async verify(message, verificationKeys) { + OpenpgpAssertion.assertMessage(message); + OpenpgpAssertion.assertKeys(verificationKeys); + + const verificationResult = await openpgp.verify({message, verificationKeys}); + await DecryptMessageService.doSignatureVerification(verificationResult.signatures); + } +} + +export default VerifyMessageService; diff --git a/src/all/background_page/service/crypto/verifyMessageSign.test.data.js b/src/all/background_page/service/crypto/verifyMessageSign.test.data.js new file mode 100644 index 00000000..ed091dc5 --- /dev/null +++ b/src/all/background_page/service/crypto/verifyMessageSign.test.data.js @@ -0,0 +1,34 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.3.0 + */ + +import {pgpKeys} from "../../../../../test/fixtures/pgpKeys/keys"; +import {OpenpgpAssertion} from "../../utils/openpgp/openpgpAssertions"; +import SignMessageService from "./signMessageService"; +import * as openpgp from 'openpgp'; + + +export const defaultData = (data = {}) => Object.assign({ + message: 'text to sign', + privateKey: pgpKeys.admin.private_decrypted +}, data); + +export const signedMessage = async(data = {}) => { + const {message, privateKey} = defaultData(data); + const messageToSign = await openpgp.createMessage({text: message}); + const adminDecryptedKey = await OpenpgpAssertion.readKeyOrFail(privateKey); + + const signedMessage = await SignMessageService.sign(messageToSign, [adminDecryptedKey]); + + return signedMessage; +}; diff --git a/src/all/background_page/service/crypto/verifyMessageSign.test.js b/src/all/background_page/service/crypto/verifyMessageSign.test.js new file mode 100644 index 00000000..c50a06a6 --- /dev/null +++ b/src/all/background_page/service/crypto/verifyMessageSign.test.js @@ -0,0 +1,69 @@ +/** + * Passbolt ~ Open source password manager for teams + * Copyright (c) Passbolt SA (https://www.passbolt.com) + * + * Licensed under GNU Affero General Public License version 3 of the or any later version. + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) + * @license https://opensource.org/licenses/AGPL-3.0 AGPL License + * @link https://www.passbolt.com Passbolt(tm) + * @since 4.3.0 + */ + +import {pgpKeys} from '../../../../../test/fixtures/pgpKeys/keys'; +import {OpenpgpAssertion} from '../../utils/openpgp/openpgpAssertions'; +import SignMessageService from "./signMessageService"; +import VerifyMessageService from './verifyMessageSign'; +import * as openpgp from 'openpgp'; +import {signedMessage} from './verifyMessageSign.test.data'; + +describe("VerifyMessageService service", () => { + it("should verify a signed message", async() => { + expect.assertions(1); + + const message = await signedMessage(); + const readSignedMessage = await openpgp.readMessage({ + armoredMessage: message // parse armored message + }); + const verificationKeys = await OpenpgpAssertion.readAllKeysOrFail([pgpKeys.admin.public]); + + await expect(VerifyMessageService.verify(readSignedMessage, verificationKeys)).resolves.not.toThrow(); + }); + + + it("should throw an error in case an invalid message", async() => { + expect.assertions(1); + + const adminDecryptedKey = await OpenpgpAssertion.readAllKeysOrFail([pgpKeys.admin.private_decrypted]); + const promise = VerifyMessageService.verify("", adminDecryptedKey); + + return expect(promise).rejects.toThrowError(new TypeError("The message should be a valid openpgp message.")); + }); + + it("should throw an error in case an invalid private key", async() => { + expect.assertions(1); + const messageToSign = await openpgp.createMessage({text: 'my-account-kit'}); + const promise = VerifyMessageService.verify(messageToSign, [""]); + + return expect(promise).rejects.toThrowError(new Error("The key should be a valid openpgp key.")); + }); + + it("should throw an error in case the signature cannot be verified", async() => { + expect.assertions(1); + + const messageToSign = await openpgp.createMessage({text: 'my-account-kit'}); + const adminDecryptedKey = await OpenpgpAssertion.readKeyOrFail(pgpKeys.admin.private_decrypted); + + const signedMessage = await SignMessageService.sign(messageToSign, [adminDecryptedKey]); + //verify the signature + const readSignedMessage = await openpgp.readMessage({ + armoredMessage: signedMessage // parse armored message + }); + + const verificationKeys = await OpenpgpAssertion.readAllKeysOrFail([pgpKeys.ada.public]); + + await expect(VerifyMessageService.verify(readSignedMessage, verificationKeys)).rejects.toThrowError(new Error("Could not find signing key with key ID 5b1b332ed06426d3")); + }); +}); diff --git a/src/all/locales/fr-FR/common.json b/src/all/locales/fr-FR/common.json index 73cee2ec..115b2ef5 100644 --- a/src/all/locales/fr-FR/common.json +++ b/src/all/locales/fr-FR/common.json @@ -44,15 +44,15 @@ "Saving permissions...": "Enregistrement des permissions...", "Saving resource": "Enregistrement de la ressource", "Server internal error. Check with your administrator.": "Erreur interne du serveur. Contactez votre administrateur.", - "Share {{count}} password_one": "Share {{count}} password", - "Share {{count}} password_other": "Share {{count}} passwords", + "Share {{count}} password_one": "Partager {{count}} mot de passe", + "Share {{count}} password_other": "Partager {{count}} mot de passes", "Sharing folder {{name}}": "Partage du dossier {{name}}", "Start sharing": "Commencer le partage", "Synchronizing keyring": "Synchronisation du porte-clés", "Synchronizing keys": "Synchronisation des clés en cours", "Tagging passwords {{taggedCount}}/{{total}}": "Étiquetage des mots de passe {{taggedCount}}/{{total}}", - "The external service is unavailable": "The external service is unavailable", - "The external service raised an error": "The external service raised an error", + "The external service is unavailable": "Le service externe est indisponible", + "The external service raised an error": "Le service externe a généré une erreur", "The folder cannot be moved inside itself.": "Le dossier ne peut pas être déplacé à l'intérieur de lui-même.", "The folder cannot be shared. Insufficient rights.": "Le dossier ne peut pas être partagé. Droits insuffisants.", "The key should be a valid openpgp armored key string.": "La clé doit être une chaîne de caractères valides OpenPGP blindée.", diff --git a/src/chrome-mv3/manifest.json b/src/chrome-mv3/manifest.json index 4aca9049..fb287401 100644 --- a/src/chrome-mv3/manifest.json +++ b/src/chrome-mv3/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "__MSG_appName__", "short_name": "passbolt", - "version": "4.3.1", + "version": "4.4.0", "description": "__MSG_appDescription__", "default_locale": "en", "externally_connectable": {}, diff --git a/src/chrome/manifest.json b/src/chrome/manifest.json index 3770f976..fe10edbb 100644 --- a/src/chrome/manifest.json +++ b/src/chrome/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "__MSG_appName__", "short_name": "passbolt", - "version": "4.3.1", + "version": "4.4.0", "description": "__MSG_appDescription__", "default_locale": "en", "externally_connectable": {}, diff --git a/src/firefox/manifest.json b/src/firefox/manifest.json index 309346ef..ba5cd9d9 100644 --- a/src/firefox/manifest.json +++ b/src/firefox/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "__MSG_appName__", "short_name": "passbolt", - "version": "4.3.1", + "version": "4.4.0", "description": "__MSG_appDescription__", "default_locale": "en", "applications": {