passbolt-browser-extension 5.10.3 → 5.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.gitlab-ci/jobs/publish.yml +11 -1
  2. package/.gitlab-ci/scripts/bin/publish_npm.sh +1 -5
  3. package/.pre-commit-config.yaml +5 -0
  4. package/CHANGELOG.md +49 -0
  5. package/RELEASE_NOTES.md +78 -104
  6. package/build-safari-extension/Passbolt-Safari-Extension/Passbolt - password manager Extension/services/fetch/fetchService.swift +90 -2
  7. package/eslint.config.mjs +0 -6
  8. package/package.json +11 -13
  9. package/src/all/background_page/controller/auth/authLogoutController.js +11 -2
  10. package/src/all/background_page/controller/auth/authLogoutController.test.js +14 -5
  11. package/src/all/background_page/controller/group/groupCreateController.js +58 -0
  12. package/src/all/background_page/controller/group/groupCreateController.test.js +67 -0
  13. package/src/all/background_page/controller/tab/openResourceUriTabController.js +60 -0
  14. package/src/all/background_page/controller/tab/openResourceUriTabController.test.js +108 -0
  15. package/src/all/background_page/error/timeoutError.js +23 -0
  16. package/src/all/background_page/event/appEvents.js +12 -0
  17. package/src/all/background_page/event/groupEvents.js +3 -10
  18. package/src/all/background_page/model/entity/resource/resourceEntity.js +1 -1
  19. package/src/all/background_page/model/entity/sso/ssoLoginUrlEntity.js +2 -0
  20. package/src/all/background_page/model/entity/sso/ssoLoginUrlEntity.test.js +2 -0
  21. package/src/all/background_page/model/group/groupModel.js +0 -15
  22. package/src/all/background_page/service/api/group/groupApiService.js +6 -3
  23. package/src/all/background_page/service/auth/authVerifyLoginChallengeService.test.js +1 -1
  24. package/src/all/background_page/service/auth/decryptUserAuthTokenService.js +2 -2
  25. package/src/all/background_page/service/group/createGroupService.js +48 -0
  26. package/src/all/background_page/service/group/createGroupService.test.js +68 -0
  27. package/src/all/background_page/service/tab/tabService.js +37 -5
  28. package/src/all/background_page/service/tab/tabService.test.js +132 -1
  29. package/src/all/background_page/utils/format/formDataUtils.js +0 -16
  30. package/src/all/background_page/utils/format/formDataUtils.test.data.js +0 -7
  31. package/src/all/background_page/utils/format/formDataUtils.test.js +1 -39
  32. package/src/all/background_page/utils/promise/promiseTimeoutService.js +3 -1
  33. package/src/all/background_page/vendors/locutus/stripslashes.js +25 -0
  34. package/src/all/background_page/vendors/locutus/stripslashes.test.js +64 -0
  35. package/src/all/background_page/vendors/locutus/urldecode.js +21 -0
  36. package/src/all/background_page/vendors/locutus/urldecode.test.js +64 -0
  37. package/src/all/background_page/vendors/locutus/urlencode.test.data.js +23 -0
  38. package/src/all/contentScripts/js/app/App.js +11 -0
  39. package/src/all/contentScripts/js/app/Login.js +11 -0
  40. package/src/chrome/manifest.json +1 -1
  41. package/src/chrome-mv3/manifest.json +1 -1
  42. package/src/firefox/manifest.json +5 -2
  43. package/src/safari/common/polyfill/fetchPolyfill.js +1 -1
  44. package/src/safari/common/polyfill/fetchPolyfill.test.js +8 -4
  45. package/src/safari/manifest.json +1 -1
@@ -39,7 +39,17 @@ publish-edge:
39
39
 
40
40
  publish-to-npmjs:
41
41
  stage: publish
42
- image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/node:22
42
+ image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/node:24
43
+ id_tokens:
44
+ NPM_ID_TOKEN:
45
+ aud: "npm:registry.npmjs.org"
46
+ SIGSTORE_ID_TOKEN:
47
+ aud: sigstore
48
+ when: manual
49
+ resource_group: publish/npm
50
+ environment:
51
+ name: publish/npm
52
+ url: https://www.npmjs.com/package/passbolt-browser-extension
43
53
  rules:
44
54
  - if: "$CI_COMMIT_TAG"
45
55
  script:
@@ -9,10 +9,6 @@ CI_SCRIPTS_DIR=$(dirname "$0")/..
9
9
  # shellcheck source=.gitlab-ci/scripts/lib/version-check.sh
10
10
  source "$CI_SCRIPTS_DIR"/lib/version-check.sh
11
11
 
12
- echo //registry.npmjs.org/:_authToken="$NPM_PUBLISH_TOKEN" > .npmrc
13
- echo email="$NPM_PUBLISH_EMAIL" >> .npmrc
14
- echo always-auth=true >> .npmrc
15
-
16
12
  if is_release_candidate "$CI_COMMIT_TAG"; then
17
13
  npm publish --tag next
18
14
  elif is_release_alpha "$CI_COMMIT_TAG"; then
@@ -23,4 +19,4 @@ elif is_stable "$CI_COMMIT_TAG"; then
23
19
  npm publish
24
20
  else
25
21
  echo "The tag format is not supported"
26
- fi
22
+ fi
@@ -0,0 +1,5 @@
1
+ repos:
2
+ - repo: https://github.com/gitleaks/gitleaks
3
+ rev: v8.30.0 # v8.30.0
4
+ hooks:
5
+ - id: gitleaks
package/CHANGELOG.md CHANGED
@@ -4,6 +4,55 @@ This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [5.11.0] - 2026-04-07
8
+ ### Added
9
+ - PB-49733 SMTP-OAUTH - WP2.1 Update SmtpSettingsService to SmtpSettingsApiService
10
+ - PB-49734 SMTP-OAUTH - WP1.1 Create the SmtpSettingsEntity
11
+ - PB-49737 SMTP-OAUTH - WP2.2 Update SmtpTestSettingsService to SmtpTestSettingsApiService
12
+ - PB-49738 SMTP-OAUTH - WP2.3 Split SmtpSettingsModel to new architecture pattern
13
+ - PB-49739 SMTP-OAUTH - WP2.4 Split SmtpTestSettingsModel to new architecture pattern
14
+ - PB-49740 SMTP-OAUTH - WP3.1 Adapt context with the new SMTP entities
15
+ - PB-49741 SMTP-OAUTH - WP3.2 Adapt ManageSmtpAdministationSettings to handle the new OAUTH fields
16
+ - PB-50058 OAuth SMTP: add the new styleguide to backend
17
+ - PB-50135 SSO with PingOne
18
+ - PB-50157 Enable avatar upload for Safari
19
+ - PB-50254 SCIM-WP1.2 Adapt form to handle the new date field and display warning message when expired
20
+ - PB-50263 Add a username selector compatible with ProxMox
21
+
22
+ ### Fixed
23
+ - PB-46678 Fix quickaccess closing issue on Safari
24
+ - PB-49237 DisplayUserBadgeMenu attention required should be displayed on Administration page served by API
25
+ - PB-49287 When deleting a user, the URL must changed not to reference the deleted user id
26
+ - PB-49476 Fix autofill for websites using identifier as name for username field
27
+ - PB-49619 Fix username input field selector for OVH
28
+ - PB-49849 Sync generator password policy with the administration after save
29
+ - PB-49866 Fix the expiry column in the resource workspace grid is not present anymore
30
+ - PB-49882 Fix username input field selector for Supermicro IPMI WebUI
31
+ - PB-50023 Fix multifield OTP selector matching hidden inputs
32
+ - PB-50077 Fix React router issue that reloads the page unexpectedly
33
+ - PB-50177 Fix autofill issues for two websites
34
+
35
+ ### Maintenance
36
+ - PB-49129 Delegate tab opening to service worker in order to send all cookie via Safari
37
+ - PB-49459 Timeouts not cleared properly when filtering resources/users grids by keywords
38
+ - PB-49705 Add missing TOTP unit tests
39
+ - PB-49730 Setup an environment for publishing to npmjs registry
40
+ - PB-49998 Add required `data_collection_permissions` for Firefox and set it to `none`
41
+ - PB-50013 Make Safari download custom avatars
42
+ - PB-50118 Major upgrade for locutus (Critical) - passbolt-browser-extension
43
+ - PB-50158 Add Safari enablement through a feature flag
44
+ - PB-50200 Move the logic of passbolt.groups.create to GroupCreateController
45
+ - PB-50201 Update group create call in groupApiService to contain "my_group_user" as urlOptions
46
+ - PB-50202 Add supported formats documentation link in export dialog
47
+ - PB-50225 Create a CreateGroupService.js file and move the create call to api service inside it
48
+ - PB-50338 - Fix phantom @babel/preset-react
49
+
50
+ ### Security
51
+ - PB-49608 Fix ReDoS vulnerability in PGP armor regex validation
52
+ - PB-50271 Fix GHSA-25h7-pfq9-p65f - HIGH CVSS3.1
53
+ - PB-50272 Fix brace-expansion vulnerabilities
54
+
55
+
7
56
  ## [5.9.0] - 2026-01-21
8
57
  ### Fixed
9
58
  - PB-43511 Display the "Migrate metadata" admin home page card icon with a 2px stroke width
package/RELEASE_NOTES.md CHANGED
@@ -1,123 +1,97 @@
1
- Passbolt 5.10.0 is the first version of Passbolt that officially supports Safari. Also, this version comes with tags in the grid and security improvements regarding CSV exports.
1
+ Passbolt 5.11.0 introduces improvements to enterprise authentication and integration capabilities, alongside continued security hardening.
2
2
 
3
- # Safari is now supported
3
+ This release adds support for OAuth-based SMTP authentication for Microsoft Exchange Online and expands SSO coverage with PingOne. It also includes the finalisation of SCIM following external audit fixes.
4
4
 
5
- Passbolt 5.10.0 adds Safari as a supported browser. Safari has its own specificities and limitations, therefore features like avatars are disabled.
5
+ ## SMTP OAuth support for Microsoft Exchange Online
6
6
 
7
- # TOTP Autofill
7
+ Passbolt 5.11 introduces OAuth 2.0 support for SMTP with Microsoft Exchange Online, replacing legacy username/password authentication.
8
8
 
9
- Passbolt 5.10.0 now automatically fills the one-time password directly into login forms, just like it does with usernames and passwords. This seamless integration simplifies your multi-factor authentication by eliminating manual copying.
9
+ Administrators can configure the OAuth (Client Credentials) method by registering an application in Microsoft Entra ID and providing the required tenant ID, client ID, client secret, and service account email.
10
10
 
11
- # Tags are visible in the grid
11
+ At runtime, Passbolt retrieves short-lived access tokens to authenticate SMTP connections without user interaction, improving security and aligning with modern authentication standards.
12
12
 
13
- This version also releases modernization of the tag codebase. This allows us to present these tags in the grid but also paves the way for further improvement of this feature.
14
13
 
15
- # CSV export security update
14
+ ## PingOne SSO support (Passbolt Pro)
16
15
 
17
- CSV export has been updated to reinforce Passbolt's security postures. Some spreadsheet software that supports CSV also executes formulas when opening these files. It's a security issue that has been tackled in this version in 2 ways:
18
- the CSV exports are disabled by default (import is still working) and can be reenabled via a server configuration
19
- When CSV exports are enabled, a confirmation checkbox is displayed to ensure the users know what are the risks of this kind of export. Exported values are not modified to keep data integrity
16
+ Passbolt 5.11 adds support for PingOne as a new SSO provider, enabling organisations to authenticate users via their existing Ping Identity infrastructure.
20
17
 
21
- # React 18 migration
18
+ The integration is based on OpenID Connect (OIDC) using the Authorization Code flow, with Passbolt delegating authentication to PingOne and receiving a verified user identity via ID tokens.
22
19
 
23
- The migration to React 18 is a significant step toward modernizing the application's entire codebase. This update improves the code and brings performance optimizations for our users.
20
+ Administrators can configure PingOne from the SSO settings using the required environment ID, client ID, client secret, and base URL, with a dry-run option available to validate the setup before activation. Once enabled, users are redirected to PingOne for authentication and seamlessly logged into Passbolt, including during account recovery.
24
21
 
25
- # Conclusion
26
- 3 long awaited features are finally out: Safari, TOTP autofill and tags in the grid.
22
+ This addition expands Passbolt’s SSO coverage for enterprise environments and removes a key adoption blocker for organisations standardised on Ping Identity.
23
+
24
+ ## SCIM: audit fixes and general availability (Passbolt Pro)
25
+
26
+ Following the external security audit conducted by Cure53, this release includes fixes addressing the identified findings in the SCIM provisioning implementation.
27
+
28
+ With these changes, SCIM is now considered stable and exits beta.
29
+
30
+ The audit-driven improvements strengthen validation, error handling, and overall robustness of the provisioning flow. SCIM is now ready for production use in environments requiring automated user lifecycle management.
31
+
32
+ ## Security improvements
33
+
34
+ This release continues the ongoing security hardening effort across the platform.
35
+
36
+ In addition to the SCIM audit fixes, improvements have been made to align with external audit recommendations and reduce potential attack surface in authentication and integration layers.
37
+
38
+ ## Maintenance & performance
39
+
40
+ This release includes general performance improvements, particularly around background job processing and email delivery workflows.
41
+
42
+ Email-related operations are now more efficient and better distributed, reducing bottlenecks in high-load environments.
43
+
44
+ As usual, additional optimisations are already in progress for upcoming releases.
45
+
46
+ ## Conclusion
47
+
48
+ As usual, the release is also packed with additional improvements and fixes. Check out the changelog to learn more.
49
+
50
+ Many thanks to everyone who provided feedback, reported bugs, and contributed to making passbolt better!
27
51
 
28
52
  ### Added
29
- - PB-28063 Activate Safari support in the styleguide
30
- - PB-29275 SAF - WP2.10 Add Safari as supported extension
31
- - PB-29292 SAF - WP2.11 Fix quickaccess opening on Safari
32
- - PB-29605 SAF - WP2.7 Fix detached quickaccess not being closed after "use on this page" click
33
- - PB-36503 Browser extension causes performance degradation on some websites
34
- - PB-36503 Browser extension causes performance degradation on some websites
35
- - PB-43353 SAF - WP2.8 Fix file download on Safari
36
- - PB-43355 SAF - WP2.9 Fix quickaccess animations
37
- - PB-43997 SAF - WP1 Update the Safari browser extension build
38
- - PB-44342 SAF - WP2.1 Provide Safari with its own polyfill
39
- - PB-44343 SAF - WP2.2 Remove unsupported index.js callback
40
- - PB-44345 SAF - WP2.4 fix the CSS injection in styleguide.js
41
- - PB-45869 SAF - WP2.13 Implement file download using the native messaging
42
- - PB-45870 SAF - WP2.14 Implement a custom fetch using the native messaging
43
- - PB-46265 SAF - WP2.15 Fix authentication with MFA in the quickaccess
44
- - PB-46679 SAF - Fix bold font rendering
45
- - PB-47765 Tags modernization
46
- - PB-47777 Migrate tags logic from components to TagServiceWorkerService
47
- - PB-47789 REACT18 - Update ReactDom render to createRoot
48
- - PB-47992 REACT 18 - migration of ResourceWorkspaceContext
49
- - PB-48158 REACT 18 - Implement the migration of Dialog and Progress Contexts
50
- - PB-48240 REACT18 - UserWorkspace migration
51
- - PB-48252 REACT18 - Migrate ExtAppContext
52
- - PB-48253 SAF - Temporarily remove Avatar download to avoid user being signed out
53
- - PB-48258 SAF - Temporarily remove "upload avatar" feature
54
- - PB-48337 REACT18 - Update contexts that should use functional update
55
- - PB-48338 REACT18 - Update shared components that should use functional update
56
- - PB-48339 REACT18 - Update quickaccess components that should use functional update
57
- - PB-48340 REACT18 - Update authentication components that should use functional update
58
- - PB-48342 REACT18 - Update user setting components that should use functional update
59
- - PB-48343 REACT18 - Update administration components that should use functional update
60
- - PB-48360 REACT18 - Update resource components that should use functional update
61
- - PB-48363 REACT18 - Update user components that should use functional update
62
- - PB-48366 REACT18 - Remove await set state in contexts
63
- - PB-48384 REACT18 - Remove await setState in components and apps
64
- - PB-48404 REACT18 - Object.assign should use functional set state for context
65
- - PB-48408 CSV - WP1.2 Add a warning message when user is selecting a CSV format on the button
66
- - PB-48416 CSV - WP2.9 Check if the setting is enabled when displaying the csv format on exportFormats
67
- - PB-48419 REACT18 - Update the components to use functional setState
68
- - PB-48425 REACT18 - Form validation should not check errors in the state for component
69
- - PB-48470 Create ColumnTagsModel component
70
- - PB-48471 TAGRID-1.2 Create CellTags component and make it resizable
71
- - PB-48472 TAGRID-1.3 Add ColumnTagsModel and CellTags to DisplayResourcesList
72
- - PB-48473 TAGRID-1.4 Clicking on a tag should filter the workspace
73
- - PB-48521 Harmonise tags style
74
- - PB-48553 SAF - Use webNavigation instead of tab update to improve navigation performances
75
- - PB-49070 REACT18 - Migrate SSOContext for react-extension
76
- - PB-49085 REACT18 - Migrate tests to remove legacyRoot true
77
- - PB-49092 TAGRID-1.6 Hovering the tag on the resource detail should display tooltip
78
- - PB-49106 CSV - WP2.2 Implement the exportPoliciesSettingsEntity
79
- - PB-49107 CSV - WP2.3 Implement the exportPoliciesSettingsApiService
80
- - PB-49108 CSV - WP2.4 Implement the findExportPoliciesSettingsService
81
- - PB-49109 CSV - WP2.5 Implement findExportPoliciesSettingsController
82
- - PB-49110 CSV - WP2.7 Implement exportPoliciesSettingsServiceWorkerService
83
- - PB-49134 REACT18 - Migrate ApiAppContext
84
- - PB-49137 CSV - WP2.8 Implement the ExportPoliciesContext
85
- - PB-49138 CSV - WP2.6 Add event to find export policies settings
86
- - PB-49172 REACT18 - Rename method in DisplaySelfRegistrationAdminstration
87
- - PB-49248 REACT 18 - Revert functional setstate
88
- - PB-49262 REACT18 - revert functional setstate in contexts and components
89
- - PB-49270 SAF - Fix Safari Users settings for Duo MFA configuration
90
- - PB-49293 TOTP Autofill
91
- - PB-49294 Send TOTP through port to fill from in-form menu or quickaccess
53
+ - PB-49733 SMTP-OAUTH - WP2.1 Update SmtpSettingsService to SmtpSettingsApiService
54
+ - PB-49734 SMTP-OAUTH - WP1.1 Create the SmtpSettingsEntity
55
+ - PB-49737 SMTP-OAUTH - WP2.2 Update SmtpTestSettingsService to SmtpTestSettingsApiService
56
+ - PB-49738 SMTP-OAUTH - WP2.3 Split SmtpSettingsModel to new architecture pattern
57
+ - PB-49739 SMTP-OAUTH - WP2.4 Split SmtpTestSettingsModel to new architecture pattern
58
+ - PB-49740 SMTP-OAUTH - WP3.1 Adapt context with the new SMTP entities
59
+ - PB-49741 SMTP-OAUTH - WP3.2 Adapt ManageSmtpAdministationSettings to handle the new OAUTH fields
60
+ - PB-50058 OAuth SMTP: add the new styleguide to backend
61
+ - PB-50135 SSO with PingOne
62
+ - PB-50157 Enable avatar upload for Safari
63
+ - PB-50254 SCIM-WP1.2 Adapt form to handle the new date field and display warning message when expired
64
+ - PB-50263 Add a username selector compatible with ProxMox
92
65
 
93
66
  ### Fixed
94
- - PB-48468 Fix layout when an announcement is visible
95
- - PB-49330 Alignment issues in 2FA Yubikey login page
67
+ - PB-46678 Fix quickaccess closing issue on Safari
68
+ - PB-49237 DisplayUserBadgeMenu attention required should be displayed on Administration page served by API
69
+ - PB-49287 When deleting a user, the URL must changed not to reference the deleted user id
70
+ - PB-49476 Fix autofill for websites using identifier as name for username field
71
+ - PB-49619 Fix username input field selector for OVH
72
+ - PB-49849 Sync generator password policy with the administration after save
73
+ - PB-49866 Fix the expiry column in the resource workspace grid is not present anymore
74
+ - PB-49882 Fix username input field selector for Supermicro IPMI WebUI
75
+ - PB-50023 Fix multifield OTP selector matching hidden inputs
76
+ - PB-50077 Fix React router issue that reloads the page unexpectedly
77
+ - PB-50177 Fix autofill issues for two websites
96
78
 
97
79
  ### Maintenance
98
- - PB-47191 Review Dependabot alert for useless regular expression escape in browser extension
99
- - PB-47542 Add unit tests to roleApiService
100
- - PB-47713 REACT18- 10.2 Implement migration for QuickAccess
101
- - PB-48088 Remove console errors related to pagemod page detection
102
- - PB-48242 Remove dev phantom dependencies
103
- - PB-48375 Add tests to gpg user id parser
104
- - PB-48467 Add unit test to improve coverage on Allowed Content type page
105
- - PB-49472 Remove unnecessary permissions from entitlements and project
106
- - PB-49631 Optimize getFirst function
80
+ - PB-49129 Delegate tab opening to service worker in order to send all cookie via Safari
81
+ - PB-49459 Timeouts not cleared properly when filtering resources/users grids by keywords
82
+ - PB-49705 Add missing TOTP unit tests
83
+ - PB-49730 Setup an environment for publishing to npmjs registry
84
+ - PB-49998 Add required `data_collection_permissions` for Firefox and set it to `none`
85
+ - PB-50013 Make Safari download custom avatars
86
+ - PB-50118 Major upgrade for locutus (Critical) - passbolt-browser-extension
87
+ - PB-50158 Add Safari enablement through a feature flag
88
+ - PB-50200 Move the logic of passbolt.groups.create to GroupCreateController
89
+ - PB-50201 Update group create call in groupApiService to contain "my_group_user" as urlOptions
90
+ - PB-50202 Add supported formats documentation link in export dialog
91
+ - PB-50225 Create a CreateGroupService.js file and move the create call to api service inside it
92
+ - PB-50338 - Fix phantom @babel/preset-react
107
93
 
108
94
  ### Security
109
- - PB-48025 Major upgrade for pino (Medium) - passbolt-browser-extension
110
- - PB-48039 Small upgrade for validator (Medium) - styleguide
111
- - PB-48256 Small upgrade for lodash-es (Medium) - all-projects
112
- - PB-48257 Small upgrade for lodash (Medium) - all projects
113
- - PB-48527 Small upgrade for locutus (Critical) - passbolt-windows
114
- - PB-48535 NPM - Remove now unnecessary overrides in package.json for styleguide and bext
115
- - PB-49119 Remove dev phantom dependencies - node-fetch
116
- - PB-49120 Remove dev phantom dependencies - history
117
- - PB-49121 Remove dev phantom dependencies - expect
118
- - PB-49369 Fix GCVE-0-2026-2391 - Medium CVSS4.0
119
- - PB-49372 Fix GCVE-0-2025-68458 & GCVE-0-2025-68157 - LOW CVSS3.1
120
- - PB-49373 Fix GCVE-0-2026-25547 - CRITICAL CVSS4.0
121
- - PB-49432 Fix GCVE-0-2025-69873 - MEDIUM CVSS4.0
122
- - PB-49452 Fix GHSA-3ppc-4f35-3m26 - HIGH CVSS4.0
123
- - PB-49454 Update CSPs to allow inline <style> in SVGs
95
+ - PB-49608 Fix ReDoS vulnerability in PGP armor regex validation
96
+ - PB-50271 Fix GHSA-25h7-pfq9-p65f - HIGH CVSS3.1
97
+ - PB-50272 Fix brace-expansion vulnerabilities
@@ -23,6 +23,14 @@
23
23
 
24
24
  import Foundation
25
25
 
26
+ private extension Data {
27
+ mutating func append(_ string: String) {
28
+ if let data = string.data(using: .utf8) {
29
+ append(data)
30
+ }
31
+ }
32
+ }
33
+
26
34
  final class FetchService {
27
35
 
28
36
  // Runs a fetch on the API with profile isolation.
@@ -35,19 +43,31 @@ final class FetchService {
35
43
  // profileUUID: The Safari profile UUID for session isolation
36
44
  static func fetch(url: URL, options: [String: Any], profileUUID: String) async throws -> [String: Any] {
37
45
  let method = options["method"] as? String ?? "GET"
38
- let body = options["body"] as? String ?? ""
39
46
  let headers = options["headers"] as? [String: String] ?? [:]
40
47
  let cookies = options["cookies"] as? String
41
48
 
42
49
  var httpRequest = URLRequest(url: url)
43
50
  httpRequest.httpMethod = method
44
- httpRequest.httpBody = body.data(using: .utf8)
51
+
52
+ // Build the body: structured FormData array (may contain files) or plain string
53
+ if let formDataArray = options["body"] as? [[String: Any]] {
54
+ let boundary = UUID().uuidString
55
+ httpRequest.httpBody = buildMultipartBody(formDataArray: formDataArray, boundary: boundary)
56
+ httpRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
57
+ } else {
58
+ let body = options["body"] as? String ?? ""
59
+ httpRequest.httpBody = body.data(using: .utf8)
60
+ }
45
61
 
46
62
  // SECURITY: Add cache-control headers to prevent response caching
47
63
  httpRequest.setValue("no-cache, no-store, must-revalidate", forHTTPHeaderField: "Cache-Control")
48
64
  httpRequest.setValue("no-cache", forHTTPHeaderField: "Pragma")
49
65
 
50
66
  for header in headers {
67
+ // Skip Content-Type when we already set it for multipart
68
+ if header.key.lowercased() == "content-type" && httpRequest.value(forHTTPHeaderField: "Content-Type")?.contains("multipart") == true {
69
+ continue
70
+ }
51
71
  httpRequest.setValue(String(describing: header.value), forHTTPHeaderField: String(describing: header.key))
52
72
  }
53
73
 
@@ -59,6 +79,74 @@ final class FetchService {
59
79
  return try await doFetch(request: httpRequest, profileUUID: profileUUID)
60
80
  }
61
81
 
82
+ /// Build a multipart/form-data body from the structured FormData array sent by JavaScript.
83
+ /// Each entry has: key, value, type ("SCALAR" or "FILE"), and optionally name (for files).
84
+ /// File values use the data URL format: "data:<mimeType>;base64,<base64Data>"
85
+ private static func buildMultipartBody(formDataArray: [[String: Any]], boundary: String) -> Data {
86
+ var body = Data()
87
+
88
+ for entry in formDataArray {
89
+ guard let key = entry["key"] as? String,
90
+ let value = entry["value"] as? String,
91
+ let type = entry["type"] as? String else {
92
+ continue
93
+ }
94
+
95
+ let sanitizedKey = sanitizeHeaderValue(key)
96
+ body.append("--\(boundary)\r\n")
97
+
98
+ if type == "FILE" {
99
+ let rawFilename = entry["name"] as? String ?? "file"
100
+ let sanitizedFilename = sanitizeFilename(rawFilename)
101
+ let (mimeType, fileData) = decodeDataURL(value)
102
+ let sanitizedMimeType = sanitizeHeaderValue(mimeType)
103
+
104
+ body.append("Content-Disposition: form-data; name=\"\(sanitizedKey)\"; filename=\"\(sanitizedFilename)\"\r\n")
105
+ body.append("Content-Type: \(sanitizedMimeType)\r\n\r\n")
106
+ body.append(fileData)
107
+ body.append("\r\n")
108
+ } else {
109
+ body.append("Content-Disposition: form-data; name=\"\(sanitizedKey)\"\r\n\r\n")
110
+ body.append("\(value)\r\n")
111
+ }
112
+ }
113
+
114
+ body.append("--\(boundary)--\r\n")
115
+ return body
116
+ }
117
+
118
+ /// Sanitize a filename for use in Content-Disposition headers.
119
+ /// Strips path traversal components, quotes, and CRLF characters.
120
+ private static func sanitizeFilename(_ raw: String) -> String {
121
+ let nameOnly = raw.components(separatedBy: CharacterSet(charactersIn: "/\\")).last ?? "file"
122
+ return sanitizeHeaderValue(nameOnly)
123
+ }
124
+
125
+ /// Sanitize a value interpolated into an HTTP header by removing quotes and CRLF.
126
+ private static func sanitizeHeaderValue(_ raw: String) -> String {
127
+ raw.replacingOccurrences(of: "\"", with: "")
128
+ .replacingOccurrences(of: "\r", with: "")
129
+ .replacingOccurrences(of: "\n", with: "")
130
+ }
131
+
132
+ /// Decode a data URL (e.g. "data:image/png;base64,iVBOR...") into its MIME type and binary data.
133
+ private static func decodeDataURL(_ dataURL: String) -> (mimeType: String, data: Data) {
134
+ let parts = dataURL.components(separatedBy: ",")
135
+ guard parts.count == 2,
136
+ let base64Data = Data(base64Encoded: parts[1]) else {
137
+ return ("application/octet-stream", Data())
138
+ }
139
+
140
+ // Extract MIME type from "data:image/png;base64"
141
+ let header = parts[0]
142
+ let mimeType = header
143
+ .replacingOccurrences(of: "data:", with: "")
144
+ .components(separatedBy: ";")
145
+ .first ?? "application/octet-stream"
146
+
147
+ return (mimeType, base64Data)
148
+ }
149
+
62
150
  // The actual fetch sent to the API using a profile-isolated session
63
151
  private static func doFetch(request: URLRequest, profileUUID: String) async throws -> [String: Any] {
64
152
  // SECURITY: Use profile-specific session, NEVER URLSession.shared
package/eslint.config.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import globals from "globals";
2
- import babelParser from "@babel/eslint-parser";
3
2
  import path from "path";
4
3
  import { fileURLToPath } from "url";
5
4
 
@@ -32,18 +31,13 @@ export default [
32
31
  files: ["**/*.{js,jsx,mjs,cjs}"],
33
32
 
34
33
  languageOptions: {
35
- parser: babelParser,
36
34
  ecmaVersion: 2024,
37
35
  sourceType: "module",
38
36
 
39
37
  parserOptions: {
40
- requireConfigFile: false,
41
38
  ecmaFeatures: {
42
39
  jsx: true,
43
40
  },
44
- babelOptions: {
45
- presets: ["@babel/preset-react"],
46
- },
47
41
  },
48
42
 
49
43
  globals: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "passbolt-browser-extension",
3
- "version": "5.10.3",
3
+ "version": "5.11.1",
4
4
  "license": "AGPL-3.0",
5
5
  "copyright": "Copyright 2025 Passbolt SA",
6
6
  "description": "Passbolt web extension for the open source password manager for teams",
@@ -19,10 +19,9 @@
19
19
  "ip-regex": "^5.0.0",
20
20
  "jssha": "~3.3.1",
21
21
  "kdbxweb": "2.1.1",
22
- "locutus": "~2.0.39",
23
22
  "openpgp": "^6.1.1",
24
23
  "papaparse": "^5.5.2",
25
- "passbolt-styleguide": "5.10.5",
24
+ "passbolt-styleguide": "^5.11.3",
26
25
  "react": "^18.3.1",
27
26
  "react-dom": "^18.3.1",
28
27
  "secrets-passbolt": "github:passbolt/secrets.js#v2.0.1",
@@ -33,31 +32,30 @@
33
32
  },
34
33
  "devDependencies": {
35
34
  "@babel/core": "^7.23.2",
36
- "@babel/eslint-parser": "^7.22.9",
37
35
  "@babel/helpers": "^7.26.10",
38
36
  "@babel/plugin-transform-runtime": "^7.22.9",
39
37
  "@babel/preset-env": "^7.22.9",
40
- "@babel/preset-react": "^7.22.5",
38
+ "@babel/preset-react": "^7.28.5",
41
39
  "@babel/runtime": "^7.27.0",
42
40
  "@babel/runtime-corejs3": "^7.26.10",
43
- "@eslint/js": "^9.37.0",
41
+ "@eslint/js": "^9.39.4",
44
42
  "@svgr/webpack": "^8.1.0",
45
43
  "babel-jest": "^29.6.2",
46
44
  "babel-loader": "^8.2.3",
47
45
  "buffer": "^6.0.3",
48
46
  "crx": "^5.0.1",
49
- "eslint": "^9.37.0",
47
+ "eslint": "^9.39.4",
50
48
  "eslint-config-prettier": "^10.1.8",
51
49
  "eslint-import-resolver-alias": "^1.1.2",
52
50
  "eslint-plugin-import": "^2.32.0",
53
- "eslint-plugin-jest": "^29.0.1",
54
- "eslint-plugin-n": "^17.23.1",
55
- "eslint-plugin-no-unsanitized": "^4.1.4",
56
- "eslint-plugin-prettier": "^5.5.4",
51
+ "eslint-plugin-jest": "^29.15.1",
52
+ "eslint-plugin-n": "^17.24.0",
53
+ "eslint-plugin-no-unsanitized": "^4.1.5",
54
+ "eslint-plugin-prettier": "^5.5.5",
57
55
  "eslint-plugin-promise": "^7.2.1",
58
56
  "eslint-plugin-react": "^7.37.5",
59
- "eslint-plugin-regexp": "^2.10.0",
60
- "eslint-plugin-security": "^3.0.1",
57
+ "eslint-plugin-regexp": "^3.1.0",
58
+ "eslint-plugin-security": "^4.0.0",
61
59
  "filereader": "^0.10.3",
62
60
  "formdata-node": "^6.0.3",
63
61
  "globals": "^16.4.0",
@@ -54,8 +54,17 @@ class AuthLogoutController {
54
54
  return;
55
55
  }
56
56
 
57
- const url = this.apiClientOptions.getBaseUrl().toString();
58
- await browser.tabs.update(this.worker.tab.id, { url });
57
+ /*
58
+ * Use tabs.reload instead of tabs.update to ensure the page is fully
59
+ * reloaded from the server. A tabs.update navigation may restore the
60
+ * page from the browser's Back/Forward Cache (BFCache), resulting in
61
+ * a stale page with a dead extension message port after sign-out.
62
+ *
63
+ * See: PB-50644
64
+ */
65
+ await browser.tabs.reload(this.worker.tab.id);
66
+ // const url = this.apiClientOptions.getBaseUrl().toString();
67
+ // await browser.tabs.update(this.worker.tab.id, { url });
59
68
  }
60
69
  }
61
70
 
@@ -34,7 +34,7 @@ describe("AuthLogoutController", () => {
34
34
  });
35
35
 
36
36
  it("Should sign-out the user and redirect after.", async () => {
37
- expect.assertions(3);
37
+ expect.assertions(2);
38
38
  const logoutSpy = jest.spyOn(AuthModel.prototype, "logout").mockImplementation(() => {});
39
39
 
40
40
  const worker = {
@@ -48,10 +48,19 @@ describe("AuthLogoutController", () => {
48
48
  await controller.exec(true);
49
49
 
50
50
  expect(logoutSpy).toHaveBeenCalledTimes(1);
51
- expect(browser.tabs.update).toHaveBeenCalledTimes(1);
52
- expect(browser.tabs.update).toHaveBeenCalledWith(worker.tab.id, {
53
- url: apiClientOptions.getBaseUrl().toString(),
54
- });
51
+ expect(browser.tabs.reload).toHaveBeenCalledTimes(1);
52
+ /*
53
+ * Use tabs.reload instead of tabs.update to ensure the page is fully
54
+ * reloaded from the server. A tabs.update navigation may restore the
55
+ * page from the browser's Back/Forward Cache (BFCache), resulting in
56
+ * a stale page with a dead extension message port after sign-out.
57
+ *
58
+ * See: PB-50644
59
+ */
60
+ // expect(browser.tabs.update).toHaveBeenCalledTimes(1);
61
+ // expect(browser.tabs.update).toHaveBeenCalledWith(worker.tab.id, {
62
+ // url: apiClientOptions.getBaseUrl().toString(),
63
+ // });
55
64
  });
56
65
  });
57
66
  });
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Passbolt ~ Open source password manager for teams
3
+ * Copyright (c) Passbolt SA (https://www.passbolt.com)
4
+ *
5
+ * Licensed under GNU Affero General Public License version 3 of the or any later version.
6
+ * For full copyright and license information, please see the LICENSE.txt
7
+ * Redistributions of files must retain the above copyright notice.
8
+ *
9
+ * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
10
+ * @license https://opensource.org/licenses/AGPL-3.0 AGPL License
11
+ * @link https://www.passbolt.com Passbolt(tm)
12
+ * @since 5.11.0
13
+ */
14
+ import CreateGroupService from "../../service/group/createGroupService";
15
+ import GroupEntity from "../../model/entity/group/groupEntity";
16
+
17
+ class GroupCreateController {
18
+ /**
19
+ * GroupCreateController constructor
20
+ *
21
+ * @param {Worker} worker
22
+ * @param {string} requestId
23
+ * @param {ApiClientOptions} apiClientOptions the api client options
24
+ * @param {AccountEntity} account The account associated to the worker.
25
+ */
26
+ constructor(worker, requestId, apiClientOptions, account) {
27
+ this.worker = worker;
28
+ this.requestId = requestId;
29
+ this.createGroupService = new CreateGroupService(apiClientOptions, account);
30
+ }
31
+
32
+ /**
33
+ * Controller executor.
34
+ * @param {object} groupDto The group data
35
+ * @returns {Promise<void>}
36
+ */
37
+ async _exec(groupDto) {
38
+ try {
39
+ const group = await this.exec(groupDto);
40
+ this.worker.port.emit(this.requestId, "SUCCESS", group);
41
+ } catch (error) {
42
+ console.error(error);
43
+ this.worker.port.emit(this.requestId, "ERROR", error);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Create a group.
49
+ * @param {object} groupDto The group data
50
+ * @returns {Promise<GroupEntity>}
51
+ */
52
+ async exec(groupDto) {
53
+ const groupEntity = new GroupEntity(groupDto);
54
+ return this.createGroupService.create(groupEntity);
55
+ }
56
+ }
57
+
58
+ export default GroupCreateController;