passbolt-browser-extension 4.7.0 → 4.7.3-rc.0

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,15 @@ All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
5
  ## [Unreleased]
6
+ ## [4.7.3] - 2024-05-07
7
+ ### Maintenance
8
+ - PB-33235 Convert formData file into a json serializable in offscreen
9
+ - PB-33297 Extension update available should store the state if user signed in
10
+
11
+ ## [4.7.1] - 2024-05-02
12
+ ### Maintenance
13
+ - PB-33225 MV3 beta rollout
14
+
6
15
  ## [4.7.0] - 2024-04-26
7
16
  ### Added
8
17
  - PB-32931 As administrator, I see SSO and Directory Sync health checks in Passbolt API Status page
@@ -1586,7 +1595,9 @@ self registration settings option in the left-side bar
1586
1595
  - AP: User with plugin installed
1587
1596
  - LU: Logged in user
1588
1597
 
1589
- [Unreleased]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.7.0...HEAD
1598
+ [Unreleased]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.7.3...HEAD
1599
+ [4.7.3]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.7.1...v4.7.3
1600
+ [4.7.1]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.7.0...v4.7.1
1590
1601
  [4.7.0]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.6.2...v4.7.0
1591
1602
  [4.6.2]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.6.0...v4.6.2
1592
1603
  [4.6.0]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.5.2...v4.6.0
package/RELEASE_NOTES.md CHANGED
@@ -1,72 +1,8 @@
1
1
  Song: https://www.youtube.com/watch?v=3L4YrGaR8E4
2
2
 
3
- Passbolt v4.7 is a maintenance release that resolves multiple issues identified by the community.
4
- Furthermore, this release supports the commitment to improving customization options and integration features, making it easier for organizations to tailor the system to their specific needs.
5
-
6
- A key enhancement in this release is the ability for administrators to use custom SSL certificates for SMTP and Users directory server connections (PRO only).
7
- These long-awaited features are particularly beneficial for organizations operating in air-gapped environments or those using their own root CAs, enabling passbolt to more securely integrate with internal communication tools.
8
- All of these customizations are visible in the API status report of the administration workspace, providing a clear and manageable overview for administrators.
9
-
10
- Moreover, the integration with user directories has been enhanced, now enabling the synchronization of user accounts using multiple fields as email identifiers.
11
- This allows organizations with heterogeneous data environments to synchronize more seamlessly with Passbolt.
12
- This improvement is part of a broader initiative aimed at modernizing the integration with your user directories.
13
- Stay tuned, more enhancements are planned for future releases.
14
-
15
- ## [4.7.0] - 2024-04-26
16
- ### Added
17
- - PB-32931 As administrator, I see SSO and Directory Sync health checks in Passbolt API Status page
18
- - PB-33065 As an administrator I can add a fallback property to map my organisation AD user username
19
- - PB-33070 Request passphrase when exporting account kit
20
-
21
- ### Fixed
22
- - PB-32420 Fix double calls to PwnedPassword API service
23
- - PB-32631 Fix healthCheck Entity to support air gapped instances
24
- - PB-33066 As AD, I should not see directorySync and SSO checks if they are disabled
25
- - PB-33067 After an unexpected error during setup, recover or account recovery, only the iframe reload and the port cannot reconnect
3
+ Passbolt v4.7.3 is a test release focused on addressing problems identified with MV3.
26
4
 
5
+ ## [4.7.3] - 2024-05-07
27
6
  ### Maintenance
28
- - PB-22623 Start service worker in an insecure environment
29
- - PB-22640 As a signed-in user the inform call to action should remain after the port is disconnected only for MV3
30
- - PB-22644 The passbolt icon should detect if the user is still connected after the service worker awake
31
- - PB-23928 Handle when the extension is updated, the webIntegration should be destroy and injected again
32
- - PB-29622 Simulate user keyboard input for autofill event
33
- - PB-29946 When the service worker is shutdown and a navigation is detected the service worker do not reconnect port and stay in error mode
34
- - PB-29965 Use a dedicated service to verify the server
35
- - PB-29966 Update apiClient to support form data body and custom header
36
- - PB-29967 Use a dedicated service to do the step challenge with the server
37
- - PB-29968 use a dedicated service to check the user authentication status
38
- - PB-29969 Use a dedicated service to logout the user
39
- - PB-29988 Update the alarm in the class StartLoopAuthSessionCheckService to use the property periodInMinutes
40
- - PB-29989 Put the alarm listener at the top level for the StartLoopAuthSessionCheckService to check the authentication status
41
- - PB-29990 Move PassphraseStorageService keep alive alarm listener in top level
42
- - PB-30272 Add message service in the app content script in order to reconnect the port from a message sent by the service worker
43
- - PB-30273 On the post logout event the service worker should reconnect port that needs to receive the post logout message
44
- - PB-30274 Add message service in the browser integration content script in order to reconnect the port from a message sent by the service worker
45
- - PB-30310 Improve invalid groups users sanitization strategy
46
- - PB-30335 Use timeout instead alarms for service worker
47
- - PB-30336 Use timeout instead alarms for promise timeout service
48
- - PB-30337 Put the alarm listener at the top level for the passphraseStorageService to flush passphrase after a time duration
49
- - PB-30341 Remove alarms for toolbar controller
50
- - PB-30342 Use timeout instead of alarm for the resource in progress cache service to flush the resource not consumed
51
- - PB-30374 Check if AuthService from styleguide is still used in the Bext otherwise remove it
52
- - PB-30375 Improve CI unit test performance by running them in band
53
- - PB-32291 Cleanup legacy code and unused passbolt.auth.is-authenticated related elements
54
- - PB-32335 Split PassphraseStorageService to put the KeepSessionAlive feature on its own service
55
- - PB-32345 Ensures on the desktop app during import account that the file to import is taken into account
56
- - PB-32597 Ensure ToolbarController are set on index.js
57
- - PB-32598 Ensure add listener from authentication event controller are set on index.js
58
- - PB-32599 Ensure add listener from StartLoopAuthSessionCheckService are set on index.js
59
- - PB-32604 Ensure add listener from on extension update available controller are set on index.js
60
- - PB-32602 Ensure add listener from user.js are set on index.js
61
- - PB-32603 Ensure add listener from ResourceInProgressCacheService are set on index.js
62
- - PB-32915 Update code to remove the destruction of the public web sign-in on port disconnected
63
- - PB-32916 Update code to remove the destruction of the setup on port disconnected
64
- - PB-32917 Update code to remove the destruction of the recover on port disconnected
65
- - PB-33018 Automate browser extension npm publication
66
- - PB-33024 Ensure only stable tags of the styleguide are published to npm
67
- - PB-33024 Ensure only stable tag of the browser extension are sent for review or publish to the store
68
- - PB-33061 Create account temporary storage
69
- - PB-33062 Use temporary account storage for setup process
70
- - PB-33063 Use temporary account storage for recover process
71
- - PB-33064 Use temporary account storage for account recovery process
72
- - PB-33068 Remove beta information for the windows app
7
+ - PB-33235 Convert formData file into a json serializable in offscreen
8
+ - PB-33297 Extension update available should store the state if user signed in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "passbolt-browser-extension",
3
- "version": "4.7.0",
3
+ "version": "4.7.3-rc.0",
4
4
  "license": "AGPL-3.0",
5
5
  "copyright": "Copyright 2022 Passbolt SA",
6
6
  "description": "Passbolt web extension for the open source password manager for teams",
@@ -13,7 +13,7 @@
13
13
  */
14
14
  import Entity from "passbolt-styleguide/src/shared/models/entity/abstract/entity";
15
15
  import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/entitySchema";
16
- import b64ToBlob from "../../../../utils/format/base64";
16
+ import Base64Utils from "../../../../utils/format/base64";
17
17
 
18
18
 
19
19
  const ENTITY_NAME = 'AvatarUpdate';
@@ -47,7 +47,7 @@ class AvatarUpdateEntity extends Entity {
47
47
  const filename = avatarBase64UpdateDto.filename;
48
48
  const fileBase64 = avatarBase64UpdateDto.fileBase64;
49
49
  const mimeType = avatarBase64UpdateDto.mimeType;
50
- const file = b64ToBlob(fileBase64, mimeType);
50
+ const file = Base64Utils.base64ToBlob(fileBase64, mimeType);
51
51
  const avatarUpdateDto = {file: file, filename: filename, mimeType: mimeType};
52
52
  return new AvatarUpdateEntity(avatarUpdateDto);
53
53
  }
@@ -20,6 +20,8 @@ import WebIntegrationPagemod from "../../pagemod/webIntegrationPagemod";
20
20
  import WorkerService from "../worker/workerService";
21
21
  import PublicWebsiteSignInPagemod from "../../pagemod/publicWebsiteSignInPagemod";
22
22
 
23
+ const PASSBOLT_EXTENSION_UPDATE = "passboltExtensionUpdate";
24
+
23
25
  class OnExtensionUpdateAvailableService {
24
26
  /**
25
27
  * Execute the OnExtensionUpdateAvailableService process
@@ -27,7 +29,7 @@ class OnExtensionUpdateAvailableService {
27
29
  */
28
30
  static async exec() {
29
31
  if (await isUserAuthenticated()) {
30
- this.shouldReload = true;
32
+ await browser.storage.session.set({[PASSBOLT_EXTENSION_UPDATE]: true});
31
33
  } else {
32
34
  await this.cleanAndReload();
33
35
  }
@@ -47,8 +49,9 @@ class OnExtensionUpdateAvailableService {
47
49
  * It triggers a runtime reload if an extension update was available while the user was signed in.
48
50
  */
49
51
  static async handleUserLoggedOut() {
50
- if (this.shouldReload) {
51
- this.shouldReload = false;
52
+ const shouldUpdate = await browser.storage.session.get(PASSBOLT_EXTENSION_UPDATE);
53
+ if (shouldUpdate && shouldUpdate[PASSBOLT_EXTENSION_UPDATE]) {
54
+ await browser.storage.session.remove(PASSBOLT_EXTENSION_UPDATE);
52
55
  await this.cleanAndReload();
53
56
  }
54
57
  }
@@ -1,33 +1,61 @@
1
1
  /**
2
- * Transforms a base 64 encoded file content into a file object.
3
- * Useful when we need to transmit a file from the content code to the add-on code.
4
- * @param string b64Data
5
- * @param string contentType
6
- * @param integer sliceSize
7
- * @returns {*}
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 4.8.0
8
13
  */
9
- function b64ToBlob(b64Data, contentType, sliceSize) {
10
- contentType = contentType || '';
11
- sliceSize = sliceSize || 512;
12
14
 
13
- const byteCharacters = atob(b64Data);
14
- const byteArrays = [];
15
+ /**
16
+ * The class that deals with Passbolt to convert base64.
17
+ */
18
+ class Base64Utils {
19
+ /**
20
+ * Transforms a base 64 encoded file content into a file object.
21
+ * Useful when we need to transmit a file from the content code to the add-on code.
22
+ * @param {string} b64Data The base64 data
23
+ * @param {string} contentType The content type
24
+ * @param {number} sliceSize The slice size
25
+ * @returns {*}
26
+ */
27
+ static base64ToBlob(b64Data, contentType = "", sliceSize = 512) {
28
+ const byteCharacters = atob(b64Data);
29
+ const byteArrays = [];
15
30
 
16
- for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
17
- const slice = byteCharacters.slice(offset, offset + sliceSize);
31
+ for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
32
+ const slice = byteCharacters.slice(offset, offset + sliceSize);
18
33
 
19
- const byteNumbers = new Array(slice.length);
20
- for (let i = 0; i < slice.length; i++) {
21
- byteNumbers[i] = slice.charCodeAt(i);
22
- }
34
+ const byteNumbers = new Array(slice.length);
35
+ for (let i = 0; i < slice.length; i++) {
36
+ byteNumbers[i] = slice.charCodeAt(i);
37
+ }
23
38
 
24
- const byteArray = new Uint8Array(byteNumbers);
39
+ const byteArray = new Uint8Array(byteNumbers);
25
40
 
26
- byteArrays.push(byteArray);
41
+ byteArrays.push(byteArray);
42
+ }
43
+
44
+ return new Blob(byteArrays, {type: contentType});
27
45
  }
28
46
 
29
- const blob = new Blob(byteArrays, {type: contentType});
30
- return blob;
47
+ /**
48
+ * Transforms a file object into a base 64 encoded file content.
49
+ * @param {Blob} blob
50
+ * @returns {Promise<string>}
51
+ */
52
+ static blobToBase64(blob) {
53
+ return new Promise(resolve => {
54
+ const reader = new FileReader();
55
+ reader.onloadend = () => resolve(reader.result);
56
+ reader.readAsDataURL(blob);
57
+ });
58
+ }
31
59
  }
32
60
 
33
- export default b64ToBlob;
61
+ export default Base64Utils;
@@ -0,0 +1,93 @@
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 4.8.0
13
+ */
14
+
15
+ import Base64Utils from "./base64";
16
+
17
+ /**
18
+ * The class that deals with Passbolt to convert formData.
19
+ */
20
+ class FormDataUtils {
21
+ /**
22
+ * Transform a form data to an array of object
23
+ * @param {FormData} formData The form data
24
+ * @return {Promise<Array<Object>>}
25
+ */
26
+ static async formDataToArray(formData) {
27
+ const formDataSerialized = [];
28
+ for (const [key, value] of formData.entries()) {
29
+ const formDataObject = {
30
+ key: key
31
+ };
32
+ // BLOB in FormData is transformed into a File
33
+ if (value instanceof File) {
34
+ formDataObject.value = await Base64Utils.blobToBase64(value);
35
+ formDataObject.name = value.name;
36
+ formDataObject.type = FormDataUtils.TYPE_FILE;
37
+ } else {
38
+ formDataObject.value = value;
39
+ formDataObject.type = FormDataUtils.TYPE_SCALAR;
40
+ }
41
+ formDataSerialized.push(formDataObject);
42
+ }
43
+ return formDataSerialized;
44
+ }
45
+
46
+ /**
47
+ * Transform an array of object to a form data
48
+ * @param {Array<Object>} array
49
+ * @return {FormData}
50
+ */
51
+ static arrayToFormData(array) {
52
+ const formData = new FormData();
53
+ array.forEach(data => {
54
+ if (data.type === FormDataUtils.TYPE_SCALAR) {
55
+ formData.append(data.key, data.value);
56
+ } else {
57
+ const base64UrlSplit = data.value.split(',');
58
+ const blobBase64 = base64UrlSplit[1];
59
+ const mimeType = base64UrlSplit[0].split(':')[1].split(';')[0];
60
+ const blob = Base64Utils.base64ToBlob(blobBase64, mimeType);
61
+ formData.append(data.key, blob, data.name);
62
+ }
63
+ });
64
+ return formData;
65
+ }
66
+
67
+ /**
68
+ * Get the type scalar
69
+ * @return {string}
70
+ */
71
+ static get TYPE_SCALAR() {
72
+ return "SCALAR";
73
+ }
74
+
75
+ /**
76
+ * Get the type file
77
+ * @return {string}
78
+ */
79
+ static get TYPE_FILE() {
80
+ return "FILE";
81
+ }
82
+
83
+ /**
84
+ * Get the type blob
85
+ * @return {string}
86
+ * @constructor
87
+ */
88
+ static get TYPE_BLOB() {
89
+ return "FILE";
90
+ }
91
+ }
92
+
93
+ export default FormDataUtils;
@@ -0,0 +1,49 @@
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 4.8.0
13
+ */
14
+
15
+ export const formDataString = () => {
16
+ const formDataBody = new FormData();
17
+ formDataBody.append("prop1", "value 1");
18
+ formDataBody.append("prop1", "value 2");
19
+ return formDataBody;
20
+ };
21
+
22
+ export const formDataFile = () => {
23
+ const formDataBody = new FormData();
24
+ const file1 = new File(['test'], "file 1", {type: 'image/png'});
25
+ const file2 = new File(['test'], "file 2", {type: 'image/png'});
26
+ formDataBody.append("file", file1, "file 1");
27
+ formDataBody.append("file", file2, "file 2");
28
+ return formDataBody;
29
+ };
30
+
31
+ export const formDataBlob = () => {
32
+ const formDataBody = new FormData();
33
+ const blob1 = new Blob(['test'], {type: 'text/plain'});
34
+ const blob2 = new Blob(['test'], {type: 'text/plain'});
35
+ formDataBody.append("blob", blob1, "blob 1");
36
+ formDataBody.append("blob", blob2, "blob 2");
37
+ return formDataBody;
38
+ };
39
+
40
+ export const formDataMixed = () => {
41
+ const formDataBody = new FormData();
42
+ formDataBody.append("prop1", "value 1");
43
+ const file = new File(['test'], "file 1", {type: 'image/png'});
44
+ formDataBody.append("file", file, "file 1");
45
+ const blob = new Blob(['test'], {type: 'text/plain'});
46
+ formDataBody.append("blob", blob, "blob 1");
47
+ return formDataBody;
48
+ };
49
+
@@ -0,0 +1,105 @@
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 4.8.0
13
+ */
14
+ import {formDataMixed, formDataString} from "./formDataUtils.test.data";
15
+ import FormDataUtils from "./formDataUtils";
16
+ import {formDataBlob, formDataFile} from "./formDataUtils.test.data";
17
+
18
+ describe("FormDataUtils", () => {
19
+ beforeEach(() => {
20
+ jest.resetModules();
21
+ });
22
+
23
+ describe("FormDataUtils::formDataToArray", () => {
24
+ it("Should create an array of scalar object", async() => {
25
+ expect.assertions(1);
26
+ // data mocked
27
+ const formData = formDataString();
28
+ // process
29
+ const arrayObject = await FormDataUtils.formDataToArray(formData);
30
+ // expectations
31
+ const expectedArray = [
32
+ {key: "prop1", value: "value 1", type: FormDataUtils.TYPE_SCALAR},
33
+ {key: "prop1", value: "value 2", type: FormDataUtils.TYPE_SCALAR}
34
+ ];
35
+ expect(arrayObject).toStrictEqual(expectedArray);
36
+ });
37
+
38
+ it("Should create an array of file object", async() => {
39
+ expect.assertions(1);
40
+ // data mocked
41
+ const formData = formDataFile();
42
+ // process
43
+ const arrayObject = await FormDataUtils.formDataToArray(formData);
44
+ // expectations
45
+ const expectedArray = [
46
+ {key: "file", value: "data:image/png;base64,dGVzdA==", name: "file 1", type: FormDataUtils.TYPE_FILE},
47
+ {key: "file", value: "data:image/png;base64,dGVzdA==", name: "file 2", type: FormDataUtils.TYPE_FILE}
48
+ ];
49
+ expect(arrayObject).toStrictEqual(expectedArray);
50
+ });
51
+
52
+ it("Should create an array of blob object", async() => {
53
+ expect.assertions(1);
54
+ // data mocked
55
+ const formData = formDataBlob();
56
+ // process
57
+ const arrayObject = await FormDataUtils.formDataToArray(formData);
58
+ // expectations
59
+ const expectedArray = [
60
+ {key: "blob", value: "data:text/plain;base64,dGVzdA==", name: "blob 1", type: FormDataUtils.TYPE_BLOB},
61
+ {key: "blob", value: "data:text/plain;base64,dGVzdA==", name: "blob 2", type: FormDataUtils.TYPE_BLOB}
62
+ ];
63
+ expect(arrayObject).toStrictEqual(expectedArray);
64
+ });
65
+
66
+ it("Should create an array of mixed object", async() => {
67
+ expect.assertions(1);
68
+ // data mocked
69
+ const formData = formDataMixed();
70
+ // process
71
+ const arrayObject = await FormDataUtils.formDataToArray(formData);
72
+ // expectations
73
+ const expectedArray = [
74
+ {key: "prop1", value: "value 1", type: FormDataUtils.TYPE_SCALAR},
75
+ {key: "file", value: "data:image/png;base64,dGVzdA==", name: "file 1", type: FormDataUtils.TYPE_FILE},
76
+ {key: "blob", value: "data:text/plain;base64,dGVzdA==", name: "blob 1", type: FormDataUtils.TYPE_BLOB}
77
+ ];
78
+ expect(arrayObject).toStrictEqual(expectedArray);
79
+ });
80
+ });
81
+
82
+ describe("FormDataUtils::arrayToFormData", () => {
83
+ it("should form the same formData string from the origin", async() => {
84
+ expect.assertions(1);
85
+ // data mocked
86
+ const formData = formDataString();
87
+ // process
88
+ const arrayObject = await FormDataUtils.formDataToArray(formData);
89
+ const formDataReceived = FormDataUtils.arrayToFormData(arrayObject);
90
+ // expectations
91
+ expect(formData).toStrictEqual(formDataReceived);
92
+ });
93
+
94
+ it("should form the same formData mixed from the origin", async() => {
95
+ expect.assertions(1);
96
+ // data mocked
97
+ const formData = formDataMixed();
98
+ // process
99
+ const arrayObject = await FormDataUtils.formDataToArray(formData);
100
+ const formDataReceived = FormDataUtils.arrayToFormData(arrayObject);
101
+ // expectations
102
+ expect(formData).toStrictEqual(formDataReceived);
103
+ });
104
+ });
105
+ });
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 2,
3
3
  "name": "__MSG_appName__",
4
4
  "short_name": "passbolt",
5
- "version": "4.7.0",
5
+ "version": "4.7.3",
6
6
  "description": "__MSG_appDescription__",
7
7
  "default_locale": "en",
8
8
  "externally_connectable": {},
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "name": "__MSG_appName__",
4
4
  "short_name": "passbolt",
5
- "version": "4.7.0",
5
+ "version": "4.7.3",
6
6
  "description": "__MSG_appDescription__",
7
7
  "default_locale": "en",
8
8
  "externally_connectable": {
@@ -13,12 +13,15 @@
13
13
  */
14
14
 
15
15
  import Validator from "validator";
16
+ import FormDataUtils from "../../../../all/background_page/utils/format/formDataUtils";
16
17
 
17
18
  export const SEND_MESSAGE_TARGET_FETCH_OFFSCREEN = "fetch-offscreen";
18
19
  export const SEND_MESSAGE_TARGET_FETCH_OFFSCREEN_RESPONSE_HANDLER = "service-worker-fetch-offscreen-response-handler";
19
20
  export const SEND_MESSAGE_TARGET_FETCH_OFFSCREEN_POLLING_HANDLER = "service-worker-fetch-offscreen-polling-handler";
20
21
  export const FETCH_OFFSCREEN_RESPONSE_TYPE_SUCCESS = "success";
21
22
  export const FETCH_OFFSCREEN_RESPONSE_TYPE_ERROR = "error";
23
+ export const FETCH_OFFSCREEN_DATA_TYPE_JSON = "JSON";
24
+ export const FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA = "FORM_DATA";
22
25
  const POLLING_COUNTER_UPDATE_LOCK = "POLLING_COUNTER_UPDATE_LOCK";
23
26
  const POLLING_PERIOD = 25_000;
24
27
 
@@ -54,7 +57,8 @@ export default class FetchOffscreenService {
54
57
  return;
55
58
  }
56
59
  const {id, resource, options} = message?.data || {};
57
-
60
+ // Update the body to fit the data type to send (JSON or FORM DATA)
61
+ options.body = options.body.dataType === FETCH_OFFSCREEN_DATA_TYPE_JSON ? options.body.data : FormDataUtils.arrayToFormData(options.body.data);
58
62
  await FetchOffscreenService.increaseAwaitingRequests();
59
63
  try {
60
64
  const response = await fetch(resource, options);
@@ -12,7 +12,7 @@
12
12
  * @since 4.7.0
13
13
  */
14
14
  import {fetchOptionsHeaders} from "../../../serviceWorker/service/network/requestFetchOffscreenService.test.data";
15
- import {SEND_MESSAGE_TARGET_FETCH_OFFSCREEN} from "./fetchOffscreenService";
15
+ import {FETCH_OFFSCREEN_DATA_TYPE_JSON, SEND_MESSAGE_TARGET_FETCH_OFFSCREEN} from "./fetchOffscreenService";
16
16
 
17
17
  export const defaultFetchMessage = message => ({
18
18
  target: SEND_MESSAGE_TARGET_FETCH_OFFSCREEN,
@@ -23,8 +23,11 @@ export const defaultFetchMessage = message => ({
23
23
  credentials: "include",
24
24
  headers: fetchOptionsHeaders(),
25
25
  body: {
26
- prop1: "value 1",
27
- prop2: "value 2"
26
+ data: {
27
+ prop1: "value 1",
28
+ prop2: "value 2"
29
+ },
30
+ dataType: FETCH_OFFSCREEN_DATA_TYPE_JSON
28
31
  }
29
32
  }
30
33
  },
@@ -11,6 +11,11 @@
11
11
  * @link https://www.passbolt.com Passbolt(tm)
12
12
  * @since 4.7.0
13
13
  */
14
+ import FormDataUtils from "../../../../all/background_page/utils/format/formDataUtils";
15
+ import {
16
+ FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA,
17
+ FETCH_OFFSCREEN_DATA_TYPE_JSON
18
+ } from "../../../offscreens/service/network/fetchOffscreenService";
14
19
 
15
20
  const {SEND_MESSAGE_TARGET_FETCH_OFFSCREEN} = require("../../../offscreens/service/network/fetchOffscreenService");
16
21
 
@@ -92,7 +97,7 @@ export class RequestFetchOffscreenService {
92
97
  RequestFetchOffscreenService.createIfNotExistOffscreenDocument);
93
98
 
94
99
  const offscreenFetchId = crypto.randomUUID();
95
- const offscreenFetchData = RequestFetchOffscreenService.buildOffscreenData(offscreenFetchId, resource, options);
100
+ const offscreenFetchData = await RequestFetchOffscreenService.buildOffscreenData(offscreenFetchId, resource, options);
96
101
 
97
102
  return new Promise((resolve, reject) => {
98
103
  // Stack the response listener callbacks.
@@ -130,19 +135,21 @@ export class RequestFetchOffscreenService {
130
135
  * @param {object} fetchOptions The fetch options, similar to the native fetch option parameter.
131
136
  * @returns {object}
132
137
  */
133
- static buildOffscreenData(id, resource, fetchOptions = {}) {
138
+ static async buildOffscreenData(id, resource, fetchOptions = {}) {
134
139
  const options = JSON.parse(JSON.stringify(fetchOptions));
135
140
 
136
141
  // Format FormData fetch options to allow its serialization.
137
142
  if (fetchOptions?.body instanceof FormData) {
138
- const formDataValues = [];
139
- for (const key of fetchOptions.body.keys()) {
140
- formDataValues.push(`${encodeURIComponent(key)}=${encodeURIComponent(fetchOptions.body.get(key))}`);
141
- }
142
- options.body = formDataValues.join('&');
143
- // Ensure the request content type reflect the content of its body.
144
- options.headers = options.headers ?? {};
145
- options.headers['Content-type'] = 'application/x-www-form-urlencoded';
143
+ const formDataSerialized = await FormDataUtils.formDataToArray(fetchOptions.body);
144
+ options.body = {
145
+ data: formDataSerialized,
146
+ dataType: FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA
147
+ };
148
+ } else {
149
+ options.body = {
150
+ data: fetchOptions.body,
151
+ dataType: FETCH_OFFSCREEN_DATA_TYPE_JSON
152
+ };
146
153
  }
147
154
 
148
155
  return {id, resource, options};
@@ -19,8 +19,12 @@ import {
19
19
  IS_FETCH_OFFSCREEN_PREFERRED_STORAGE_KEY,
20
20
  RequestFetchOffscreenService
21
21
  } from "./requestFetchOffscreenService";
22
- import {SEND_MESSAGE_TARGET_FETCH_OFFSCREEN} from "../../../offscreens/service/network/fetchOffscreenService";
22
+ import {
23
+ FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA, FETCH_OFFSCREEN_DATA_TYPE_JSON,
24
+ SEND_MESSAGE_TARGET_FETCH_OFFSCREEN
25
+ } from "../../../offscreens/service/network/fetchOffscreenService";
23
26
  import {fetchOptionsWithBodyFormData, fetchOptionWithBodyData} from "./requestFetchOffscreenService.test.data";
27
+ import FormDataUtils from "../../../../all/background_page/utils/format/formDataUtils";
24
28
 
25
29
  beforeEach(() => {
26
30
  enableFetchMocks();
@@ -108,19 +112,24 @@ describe("RequestFetchOffscreenService", () => {
108
112
  const id = crypto.randomUUID();
109
113
  const resource = "https://test.passbolt.com/passbolt-unit-test/test.json";
110
114
  const options = fetchOptionWithBodyData();
111
- const offscreenData = RequestFetchOffscreenService.buildOffscreenData(id, resource, options);
115
+ const offscreenData = await RequestFetchOffscreenService.buildOffscreenData(id, resource, options);
116
+ options.body = {
117
+ data: options.body,
118
+ dataType: FETCH_OFFSCREEN_DATA_TYPE_JSON
119
+ }
112
120
  // Ensure body remains a form data after serialization.
113
121
  expect(offscreenData).toEqual({id, resource, options});
114
122
  });
115
123
 
116
124
  it("should ensure given fetch options body will not be altered", async() => {
117
- expect.assertions(1);
125
+ expect.assertions(2);
118
126
  const id = crypto.randomUUID();
119
127
  const resource = "https://test.passbolt.com/passbolt-unit-test/test.json";
120
128
  const fetchOptions = fetchOptionsWithBodyFormData();
121
- RequestFetchOffscreenService.buildOffscreenData(id, resource, fetchOptions);
129
+ const offscreenData = await RequestFetchOffscreenService.buildOffscreenData(id, resource, fetchOptions);
122
130
  // Ensure body remains a form data after serialization.
123
- expect(fetchOptions.body).toBeInstanceOf(FormData);
131
+ expect(offscreenData.options.body.data).toBeInstanceOf(Array);
132
+ expect(offscreenData.options.body.dataType).toStrictEqual(FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA);
124
133
  });
125
134
 
126
135
  it("should transform FormData body into serialized encoded url parameters", async() => {
@@ -129,7 +138,7 @@ describe("RequestFetchOffscreenService", () => {
129
138
  const resource = "https://test.passbolt.com/passbolt-unit-test/test.json";
130
139
  const options = fetchOptionsWithBodyFormData();
131
140
 
132
- const offscreenData = RequestFetchOffscreenService.buildOffscreenData(id, resource, options);
141
+ const offscreenData = await RequestFetchOffscreenService.buildOffscreenData(id, resource, options);
133
142
  // eslint-disable-next-line object-shorthand
134
143
  const expectedOffscreenMessageData = {
135
144
  id,
@@ -138,9 +147,11 @@ describe("RequestFetchOffscreenService", () => {
138
147
  ...options,
139
148
  headers: {
140
149
  ...options.headers,
141
- "Content-type": "application/x-www-form-urlencoded" // ensure the content type reflect the body type parameter.
142
150
  },
143
- body: "prop1=value%201&prop1=value%201" // ensure the body is serialized as url encoded parameter
151
+ body: {
152
+ data: [{key: "prop1", value: "value 1", type: FormDataUtils.TYPE_SCALAR}, {key: "prop1", value: "value 2", type: FormDataUtils.TYPE_SCALAR}],
153
+ dataType: FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA
154
+ } // ensure the body is serialized as url encoded parameter
144
155
  }
145
156
  };
146
157
  expect(offscreenData).toEqual(expectedOffscreenMessageData);
@@ -172,9 +183,14 @@ describe("RequestFetchOffscreenService", () => {
172
183
  ...message.data.options,
173
184
  headers: {
174
185
  ...message.data.options.headers,
175
- "Content-type": "application/x-www-form-urlencoded" // ensure the content type reflect the body type parameter.
176
186
  },
177
- body: "prop1=value%201&prop1=value%201" // ensure the body is serialized as url encoded parameter
187
+ body: {
188
+ data: [
189
+ {key: "prop1", value: "value 1", type: FormDataUtils.TYPE_SCALAR},
190
+ {key: "prop1", value: "value 2", type: FormDataUtils.TYPE_SCALAR}
191
+ ],
192
+ dataType: FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA
193
+ }, // ensure the body is serialized as url encoded parameter
178
194
  }
179
195
  },
180
196
  };
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 2,
3
3
  "name": "__MSG_appName__",
4
4
  "short_name": "passbolt",
5
- "version": "4.7.0",
5
+ "version": "4.7.3",
6
6
  "description": "__MSG_appDescription__",
7
7
  "default_locale": "en",
8
8
  "browser_specific_settings": {
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "name": "__MSG_appName__",
4
4
  "short_name": "passbolt",
5
- "version": "4.7.0",
5
+ "version": "4.7.3",
6
6
  "description": "__MSG_appDescription__",
7
7
  "default_locale": "en",
8
8
  "externally_connectable": {},