diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index ecd1e404944..42d012d5a98 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -41,6 +41,8 @@ jobs: setup: name: Setup runs-on: ubuntu-22.04 + needs: + - check-run outputs: repo_url: ${{ steps.gen_vars.outputs.repo_url }} adj_build_number: ${{ steps.gen_vars.outputs.adj_build_number }} @@ -236,7 +238,6 @@ jobs: needs: - setup - locales-test - - check-run env: _BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} @@ -350,7 +351,7 @@ jobs: crowdin-push: name: Crowdin Push - if: github.ref == 'refs/heads/main' + if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main' runs-on: ubuntu-22.04 needs: - build @@ -399,7 +400,7 @@ jobs: - name: Check if any job failed if: | github.event_name != 'pull_request_target' - && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc') + && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-browser') && contains(needs.*.result, 'failure') run: exit 1 diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 98ba5b9fd8a..ac39ab2608b 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -42,6 +42,8 @@ jobs: setup: name: Setup runs-on: ubuntu-22.04 + needs: + - check-run outputs: package_version: ${{ steps.retrieve-package-version.outputs.package_version }} node_version: ${{ steps.retrieve-node-version.outputs.node_version }} @@ -398,13 +400,12 @@ jobs: - cli - cli-windows - snap - - check-run steps: - name: Check if any job failed working-directory: ${{ github.workspace }} if: | github.event_name != 'pull_request_target' - && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc') + && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-cli') && contains(needs.*.result, 'failure') run: exit 1 diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 83389c5bbec..221c998247f 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -40,6 +40,8 @@ jobs: electron-verify: name: Verify Electron Version runs-on: ubuntu-22.04 + needs: + - check-run steps: - name: Check out repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -61,6 +63,8 @@ jobs: setup: name: Setup runs-on: ubuntu-22.04 + needs: + - check-run outputs: package_version: ${{ steps.retrieve-version.outputs.package_version }} release_channel: ${{ steps.release-channel.outputs.channel }} @@ -251,7 +255,6 @@ jobs: runs-on: windows-2022 needs: - setup - - check-run defaults: run: shell: pwsh @@ -464,7 +467,6 @@ jobs: runs-on: macos-13 needs: - setup - - check-run env: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} @@ -1056,9 +1058,8 @@ jobs: - name: Deploy to TestFlight id: testflight-deploy if: | - (github.ref == 'refs/heads/main' - || github.ref == 'refs/heads/rc' - || github.ref == 'refs/heads/hotfix-rc-desktop') + github.event_name != 'pull_request_target' + && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-desktop') env: APP_STORE_CONNECT_TEAM_ISSUER: ${{ secrets.APP_STORE_CONNECT_TEAM_ISSUER }} APP_STORE_CONNECT_AUTH_KEY: 6TV9MKN3GP @@ -1073,9 +1074,8 @@ jobs: - name: Post message to a Slack channel id: slack-message if: | - (github.ref == 'refs/heads/main' - || github.ref == 'refs/heads/rc' - || github.ref == 'refs/heads/hotfix-rc-desktop') + github.event_name != 'pull_request_target' + && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-desktop') uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0 # v1.27.0 with: channel-id: C074F5UESQ0 @@ -1352,7 +1352,7 @@ jobs: - name: Check if any job failed if: | github.event_name != 'pull_request_target' - && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc') + && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-desktop') && contains(needs.*.result, 'failure') run: exit 1 diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 4ce5bad790f..ba4f2599f37 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -44,6 +44,8 @@ jobs: setup: name: Setup runs-on: ubuntu-22.04 + needs: + - check-run outputs: version: ${{ steps.version.outputs.value }} node_version: ${{ steps.retrieve-node-version.outputs.node_version }} @@ -67,7 +69,8 @@ jobs: build-artifacts: name: Build artifacts runs-on: ubuntu-22.04 - needs: setup + needs: + - setup env: _VERSION: ${{ needs.setup.outputs.version }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} @@ -151,7 +154,6 @@ jobs: needs: - setup - build-artifacts - - check-run strategy: fail-fast: false matrix: @@ -261,10 +263,9 @@ jobs: crowdin-push: name: Crowdin Push - if: github.ref == 'refs/heads/main' + if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main' needs: - build-artifacts - - check-run runs-on: ubuntu-22.04 steps: - name: Check out repo @@ -302,7 +303,6 @@ jobs: runs-on: ubuntu-22.04 needs: - build-artifacts - - check-run steps: - name: Login to Azure - CI Subscription uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 @@ -346,7 +346,7 @@ jobs: - name: Check if any job failed if: | github.event_name != 'pull_request_target' - && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc') + && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-web') && contains(needs.*.result, 'failure') run: exit 1 diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index 696bdb8b896..e79f6f69a36 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -436,9 +436,7 @@ export default class AutofillService implements AutofillServiceInterface { didAutofill = true; if (!options.skipLastUsed) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.cipherService.updateLastUsedDate(options.cipher.id); + await this.cipherService.updateLastUsedDate(options.cipher.id); } // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. diff --git a/apps/cli/package.json b/apps/cli/package.json index 622c1273823..8ddb5daccd2 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -80,7 +80,7 @@ "papaparse": "5.4.1", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", - "tldts": "6.1.58", + "tldts": "6.1.60", "zxcvbn": "4.4.2" } } diff --git a/apps/desktop/native-messaging-test-runner/package-lock.json b/apps/desktop/native-messaging-test-runner/package-lock.json index 7e1d7193b58..f57f067907a 100644 --- a/apps/desktop/native-messaging-test-runner/package-lock.json +++ b/apps/desktop/native-messaging-test-runner/package-lock.json @@ -14,7 +14,7 @@ "module-alias": "2.2.3", "node-ipc": "9.2.1", "ts-node": "10.9.2", - "uuid": "11.0.1", + "uuid": "11.0.3", "yargs": "17.7.2" }, "devDependencies": { @@ -421,9 +421,9 @@ "license": "MIT" }, "node_modules/uuid": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.1.tgz", - "integrity": "sha512-wt9UB5EcLhnboy1UvA1mvGPXkIIrHSu+3FmUksARfdVw9tuPf3CH/CohxO0Su1ApoKAeT6BVzAJIvjTuQVSmuQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" diff --git a/apps/desktop/native-messaging-test-runner/package.json b/apps/desktop/native-messaging-test-runner/package.json index 0c38902ea4c..ed2c4bb29cf 100644 --- a/apps/desktop/native-messaging-test-runner/package.json +++ b/apps/desktop/native-messaging-test-runner/package.json @@ -19,7 +19,7 @@ "module-alias": "2.2.3", "node-ipc": "9.2.1", "ts-node": "10.9.2", - "uuid": "11.0.1", + "uuid": "11.0.3", "yargs": "17.7.2" }, "devDependencies": { diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts index bf623e729a1..df575cc525f 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts @@ -466,7 +466,14 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { * Helper method to delete cipher. */ private async deleteCipher(): Promise { - const asAdmin = this.organization?.canEditAllCiphers; + const cipherIsUnassigned = + !this.cipher.collectionIds || this.cipher.collectionIds?.length === 0; + + // Delete the cipher as an admin when: + // - the organization allows for owners/admins to manage all collections/items + // - the cipher is unassigned + const asAdmin = this.organization?.canEditAllCiphers || cipherIsUnassigned; + if (this.cipher.isDeleted) { await this.cipherService.deleteWithServer(this.cipher.id, asAdmin); } else { diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 2a36c09dcd4..f2e05dbe30b 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -40,6 +40,7 @@ export enum FeatureFlag { LimitCollectionCreationDeletionSplit = "pm-10863-limit-collection-creation-deletion-split", CriticalApps = "pm-14466-risk-insights-critical-application", TrialPaymentOptional = "PM-8163-trial-payment", + SecurityTasks = "security-tasks", } export type AllowedFeatureFlagTypes = boolean | number | string; @@ -90,6 +91,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.LimitCollectionCreationDeletionSplit]: FALSE, [FeatureFlag.CriticalApps]: FALSE, [FeatureFlag.TrialPaymentOptional]: FALSE, + [FeatureFlag.SecurityTasks]: FALSE, } satisfies Record; export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue; diff --git a/libs/common/src/platform/services/fido2/fido2-utils.spec.ts b/libs/common/src/platform/services/fido2/fido2-utils.spec.ts index a05eab52305..9bb4ed0a4c5 100644 --- a/libs/common/src/platform/services/fido2/fido2-utils.spec.ts +++ b/libs/common/src/platform/services/fido2/fido2-utils.spec.ts @@ -4,6 +4,36 @@ describe("Fido2 Utils", () => { const asciiHelloWorldArray = [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; const b64HelloWorldString = "aGVsbG8gd29ybGQ="; + describe("bufferSourceToUint8Array(..)", () => { + it("should convert an ArrayBuffer", () => { + const buffer = new Uint8Array(asciiHelloWorldArray).buffer; + const out = Fido2Utils.bufferSourceToUint8Array(buffer); + expect(out).toEqual(new Uint8Array(asciiHelloWorldArray)); + }); + it("should convert an ArrayBuffer slice", () => { + const buffer = new Uint8Array(asciiHelloWorldArray).buffer.slice(8); + const out = Fido2Utils.bufferSourceToUint8Array(buffer); + expect(out).toEqual(new Uint8Array([114, 108, 100])); // 8th byte onwards + }); + it("should pass through an Uint8Array", () => { + const typedArray = new Uint8Array(asciiHelloWorldArray); + const out = Fido2Utils.bufferSourceToUint8Array(typedArray); + expect(out).toEqual(new Uint8Array(asciiHelloWorldArray)); + }); + it("should preserve the view of TypedArray", () => { + const buffer = new Uint8Array(asciiHelloWorldArray).buffer; + const input = new Uint8Array(buffer, 8, 1); + const out = Fido2Utils.bufferSourceToUint8Array(input); + expect(out).toEqual(new Uint8Array([114])); + }); + it("should convert different TypedArrays", () => { + const buffer = new Uint8Array(asciiHelloWorldArray).buffer; + const input = new Uint16Array(buffer, 8, 1); + const out = Fido2Utils.bufferSourceToUint8Array(input); + expect(out).toEqual(new Uint8Array([114, 108])); + }); + }); + describe("fromBufferToB64(...)", () => { it("should convert an ArrayBuffer to a b64 string", () => { const buffer = new Uint8Array(asciiHelloWorldArray).buffer; diff --git a/libs/common/src/platform/services/fido2/fido2-utils.ts b/libs/common/src/platform/services/fido2/fido2-utils.ts index c3c3eba246b..58034912978 100644 --- a/libs/common/src/platform/services/fido2/fido2-utils.ts +++ b/libs/common/src/platform/services/fido2/fido2-utils.ts @@ -1,13 +1,6 @@ export class Fido2Utils { static bufferToString(bufferSource: BufferSource): string { - let buffer: Uint8Array; - if (bufferSource instanceof ArrayBuffer || bufferSource.buffer === undefined) { - buffer = new Uint8Array(bufferSource as ArrayBuffer); - } else { - buffer = new Uint8Array(bufferSource.buffer); - } - - return Fido2Utils.fromBufferToB64(buffer) + return Fido2Utils.fromBufferToB64(Fido2Utils.bufferSourceToUint8Array(bufferSource)) .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=/g, ""); @@ -18,12 +11,10 @@ export class Fido2Utils { } static bufferSourceToUint8Array(bufferSource: BufferSource): Uint8Array { - if (bufferSource instanceof Uint8Array) { - return bufferSource; - } else if (Fido2Utils.isArrayBuffer(bufferSource)) { + if (Fido2Utils.isArrayBuffer(bufferSource)) { return new Uint8Array(bufferSource); } else { - return new Uint8Array(bufferSource.buffer); + return new Uint8Array(bufferSource.buffer, bufferSource.byteOffset, bufferSource.byteLength); } } diff --git a/libs/tools/generator/components/src/passphrase-settings.component.ts b/libs/tools/generator/components/src/passphrase-settings.component.ts index f2f1749cb62..9ab692348eb 100644 --- a/libs/tools/generator/components/src/passphrase-settings.component.ts +++ b/libs/tools/generator/components/src/passphrase-settings.component.ts @@ -102,8 +102,8 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy { const boundariesHint = this.i18nService.t( "generatorBoundariesHint", - constraints.numWords.min, - constraints.numWords.max, + constraints.numWords.min?.toString(), + constraints.numWords.max?.toString(), ); this.numWordsBoundariesHint.next(boundariesHint); }); diff --git a/libs/tools/generator/components/src/password-settings.component.ts b/libs/tools/generator/components/src/password-settings.component.ts index 677a3417b97..5f74a4840cf 100644 --- a/libs/tools/generator/components/src/password-settings.component.ts +++ b/libs/tools/generator/components/src/password-settings.component.ts @@ -171,10 +171,10 @@ export class PasswordSettingsComponent implements OnInit, OnDestroy { this.minNumber.valueChanges .pipe( map((value) => [value, value > 0] as const), - tap(([value]) => (lastMinNumber = this.numbers.value ? value : lastMinNumber)), + tap(([value, checkNumbers]) => (lastMinNumber = checkNumbers ? value : lastMinNumber)), takeUntil(this.destroyed$), ) - .subscribe(([, checked]) => this.numbers.setValue(checked, { emitEvent: false })); + .subscribe(([, checkNumbers]) => this.numbers.setValue(checkNumbers, { emitEvent: false })); let lastMinSpecial = 1; this.special.valueChanges @@ -188,10 +188,10 @@ export class PasswordSettingsComponent implements OnInit, OnDestroy { this.minSpecial.valueChanges .pipe( map((value) => [value, value > 0] as const), - tap(([value]) => (lastMinSpecial = this.special.value ? value : lastMinSpecial)), + tap(([value, checkSpecial]) => (lastMinSpecial = checkSpecial ? value : lastMinSpecial)), takeUntil(this.destroyed$), ) - .subscribe(([, checked]) => this.special.setValue(checked, { emitEvent: false })); + .subscribe(([, checkSpecial]) => this.special.setValue(checkSpecial, { emitEvent: false })); // `onUpdated` depends on `settings` because the UserStateSubject is asynchronous; // subscribing directly to `this.settings.valueChanges` introduces a race condition. diff --git a/package-lock.json b/package-lock.json index 1ba38d10dc8..e71e8c387d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,7 +68,7 @@ "qrious": "4.0.2", "rxjs": "7.8.1", "tabbable": "6.2.0", - "tldts": "6.1.58", + "tldts": "6.1.60", "utf-8-validate": "6.0.5", "zone.js": "0.14.10", "zxcvbn": "4.4.2" @@ -225,7 +225,7 @@ "papaparse": "5.4.1", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", - "tldts": "6.1.58", + "tldts": "6.1.60", "zxcvbn": "4.4.2" }, "bin": { @@ -36093,21 +36093,21 @@ } }, "node_modules/tldts": { - "version": "6.1.58", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.58.tgz", - "integrity": "sha512-MQJrJhjHOYGYb8DobR6Y4AdDbd4TYkyQ+KBDVc5ODzs1cbrvPpfN1IemYi9jfipJ/vR1YWvrDli0hg1y19VRoA==", + "version": "6.1.60", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.60.tgz", + "integrity": "sha512-TYVHm7G9NCnhgqOsFalbX6MG1Po5F4efF+tLfoeiOGQq48Oqgwcgz8upY2R1BHWa4aDrj28RYx0dkYJ63qCFMg==", "license": "MIT", "dependencies": { - "tldts-core": "^6.1.58" + "tldts-core": "^6.1.60" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.58", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.58.tgz", - "integrity": "sha512-dR936xmhBm7AeqHIhCWwK765gZ7dFyL+IqLSFAjJbFlUXGMLCb8i2PzlzaOuWBuplBTaBYseSb565nk/ZEM0Bg==", + "version": "6.1.60", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.60.tgz", + "integrity": "sha512-XHjoxak8SFQnHnmYHb3PcnW5TZ+9ErLZemZei3azuIRhQLw4IExsVbL3VZJdHcLeNaXq6NqawgpDPpjBOg4B5g==", "license": "MIT" }, "node_modules/tmp": { diff --git a/package.json b/package.json index 368da367e85..282a63f2351 100644 --- a/package.json +++ b/package.json @@ -202,7 +202,7 @@ "qrious": "4.0.2", "rxjs": "7.8.1", "tabbable": "6.2.0", - "tldts": "6.1.58", + "tldts": "6.1.60", "utf-8-validate": "6.0.5", "zone.js": "0.14.10", "zxcvbn": "4.4.2"