diff --git a/.claude/prompts/review-code.md b/.claude/prompts/review-code.md index 4e5f40b2743..1888b7cd503 100644 --- a/.claude/prompts/review-code.md +++ b/.claude/prompts/review-code.md @@ -1,25 +1,57 @@ -Please review this pull request with a focus on: +# Bitwarden Clients Repo Code Review - Careful Consideration Required -- Code quality and best practices -- Potential bugs or issues -- Security implications -- Performance considerations +## Think Twice Before Recommending -Note: The PR branch is already checked out in the current working directory. +Angular has multiple valid patterns. Before suggesting changes: -Provide a comprehensive review including: +- **Consider the context** - Is this code part of an active modernization effort? +- **Check for established patterns** - Look for similar implementations in the codebase +- **Avoid premature optimization** - Don't suggest refactoring stable, working code without clear benefit +- **Respect incremental progress** - Teams may be modernizing gradually with feature flags -- Summary of changes since last review -- Critical issues found (be thorough) -- Suggested improvements (be thorough) -- Good practices observed (be concise - list only the most notable items without elaboration) -- Action items for the author -- Leverage collapsible
sections where appropriate for lengthy explanations or code snippets to enhance human readability +## Angular Modernization - Handle with Care -When reviewing subsequent commits: +**Control Flow Syntax (@if, @for, @switch):** -- Track status of previously identified issues (fixed/unfixed/reopened) -- Identify NEW problems introduced since last review -- Note if fixes introduced new issues +- When you see legacy structural directives (*ngIf, *ngFor), consider whether modernization is in scope +- Do not mandate changes to stable code unless part of the PR's objective +- If suggesting modernization, acknowledge it's optional unless required by PR goals -IMPORTANT: Be comprehensive about issues and improvements. For good practices, be brief - just note what was done well without explaining why or praising excessively. +**Standalone Components:** + +- New components should be standalone whenever feasible, but do not flag existing NgModule components as issues +- Legacy patterns exist for valid reasons - consider modernization effort vs benefit + +**Typed Forms:** + +- Recommend typed forms for NEW form code +- Don't suggest rewriting working untyped forms unless they're being modified + +## Tailwind CSS - Critical Pattern + +**tw- prefix is mandatory** - This is non-negotiable and should be flagged as ❌ major finding: + +- Missing tw- prefix breaks styling completely +- Check ALL Tailwind classes in modified files + +## Rust SDK Adoption - Tread Carefully + +When reviewing cipher operations: + +- Look for breaking changes in the TypeScript → Rust boundary +- Verify error handling matches established patterns +- Don't suggest alternative SDK patterns without strong justification + +## Component Library First + +Before suggesting custom implementations: + +- Check if Bitwarden's component library already provides the functionality +- Prefer existing components over custom Tailwind styling +- Don't add UI complexity that the component library already solves + +## When in Doubt + +- **Ask questions** (💭) rather than making definitive recommendations +- **Flag for human review** (⚠️) if you're uncertain +- **Acknowledge alternatives** exist when suggesting improvements diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 39e968d941b..ed7fcac96e6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -47,12 +47,12 @@ bitwarden_license/bit-web/src/app/dirt @bitwarden/team-data-insights-and-reporti libs/dirt @bitwarden/team-data-insights-and-reporting-dev libs/common/src/dirt @bitwarden/team-data-insights-and-reporting-dev -## Localization/Crowdin (Platform and Tools team) -apps/browser/src/_locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev -apps/browser/store/locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev -apps/cli/src/locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev -apps/desktop/src/locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev -apps/web/src/locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev +## Localization/Crowdin (Platform team) +apps/browser/src/_locales @bitwarden/team-platform-dev +apps/browser/store/locales @bitwarden/team-platform-dev +apps/cli/src/locales @bitwarden/team-platform-dev +apps/desktop/src/locales @bitwarden/team-platform-dev +apps/web/src/locales @bitwarden/team-platform-dev ## Vault team files ## apps/browser/src/vault @bitwarden/team-vault-dev diff --git a/.github/renovate.json5 b/.github/renovate.json5 index ae7c2b023cb..6e142edf8a7 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -187,7 +187,6 @@ "json5", "keytar", "libc", - "log", "lowdb", "mini-css-extract-plugin", "napi", @@ -216,6 +215,8 @@ "simplelog", "style-loader", "sysinfo", + "tracing", + "tracing-subscriber", "ts-node", "ts-loader", "tsconfig-paths-webpack-plugin", @@ -230,6 +231,7 @@ "webpack-node-externals", "widestring", "windows", + "windows-core", "windows-future", "windows-registry", "zbus", @@ -254,6 +256,11 @@ groupName: "zbus", matchPackageNames: ["zbus", "zbus_polkit"], }, + { + // We need to group all windows-related packages together to avoid build errors caused by version incompatibilities. + groupName: "windows", + matchPackageNames: ["windows", "windows-core", "windows-future", "windows-registry"], + }, { // We group all webpack build-related minor and patch updates together to reduce PR noise. // We include patch updates here because we want PRs for webpack patch updates and it's in this group. @@ -356,6 +363,8 @@ "@types/jsdom", "@types/papaparse", "@types/zxcvbn", + "async-trait", + "clap", "jsdom", "jszip", "oidc-client-ts", @@ -428,5 +437,11 @@ description: "Higher versions of lowdb do not need separate types", }, ], - ignoreDeps: ["@types/koa-bodyparser", "bootstrap", "node-ipc", "@bitwarden/sdk-internal"], + ignoreDeps: [ + "@types/koa-bodyparser", + "bootstrap", + "node-ipc", + "@bitwarden/sdk-internal", + "@bitwarden/commercial-sdk-internal", + ], } diff --git a/.github/workflows/alert-ddg-files-modified.yml b/.github/workflows/alert-ddg-files-modified.yml index 4acab6b1c62..90c055a97b8 100644 --- a/.github/workflows/alert-ddg-files-modified.yml +++ b/.github/workflows/alert-ddg-files-modified.yml @@ -14,7 +14,7 @@ jobs: pull-requests: write steps: - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: fetch-depth: 0 persist-credentials: false diff --git a/.github/workflows/auto-branch-updater.yml b/.github/workflows/auto-branch-updater.yml index dcd031af0de..02176b3169e 100644 --- a/.github/workflows/auto-branch-updater.yml +++ b/.github/workflows/auto-branch-updater.yml @@ -30,7 +30,7 @@ jobs: run: echo "branch=${GITHUB_REF#refs/heads/}" >> "$GITHUB_OUTPUT" - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: 'eu-web-${{ steps.setup.outputs.branch }}' fetch-depth: 0 diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 1c805e8efbe..ab932c561ba 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -55,7 +55,7 @@ jobs: has_secrets: ${{ steps.check-secrets.outputs.has_secrets }} steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -94,7 +94,7 @@ jobs: working-directory: apps/browser steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -146,7 +146,7 @@ jobs: _NODE_VERSION: ${{ needs.setup.outputs.node_version }} steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -193,7 +193,7 @@ jobs: zip -r browser-source.zip browser-source - name: Upload browser source - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{matrix.license_type.archive_name_prefix}}browser-source-${{ env._BUILD_NUMBER }}.zip path: browser-source.zip @@ -218,6 +218,7 @@ jobs: source_archive_name_prefix: "" archive_name_prefix: "" npm_command_prefix: "dist:" + npm_package_dev_prefix: "package:dev:" readable: "open source license" type: "oss" - build_prefix: "bit-" @@ -225,6 +226,7 @@ jobs: source_archive_name_prefix: "bit-" archive_name_prefix: "bit-" npm_command_prefix: "dist:bit:" + npm_package_dev_prefix: "package:bit:dev:" readable: "commercial license" type: "commercial" browser: @@ -232,6 +234,8 @@ jobs: npm_command_suffix: "chrome" archive_name: "dist-chrome.zip" artifact_name: "dist-chrome-MV3" + artifact_name_dev: "dev-chrome-MV3" + archive_name_dev: "dev-chrome.zip" - name: "edge" npm_command_suffix: "edge" archive_name: "dist-edge.zip" @@ -250,7 +254,7 @@ jobs: artifact_name: "dist-opera-MV3" steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -268,7 +272,7 @@ jobs: npm --version - name: Download browser source - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: ${{matrix.license_type.source_archive_name_prefix}}browser-source-${{ env._BUILD_NUMBER }}.zip @@ -332,16 +336,29 @@ jobs: working-directory: browser-source/apps/browser - name: Upload extension artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{ matrix.license_type.artifact_prefix }}${{ matrix.browser.artifact_name }}-${{ env._BUILD_NUMBER }}.zip path: browser-source/apps/browser/dist/${{matrix.license_type.archive_name_prefix}}${{ matrix.browser.archive_name }} if-no-files-found: error + - name: Package dev extension + if: ${{ matrix.browser.archive_name_dev != '' }} + run: npm run ${{ matrix.license_type.npm_package_dev_prefix }}${{ matrix.browser.npm_command_suffix }} + working-directory: browser-source/apps/browser + + - name: Upload dev extension artifact + if: ${{ matrix.browser.archive_name_dev != '' }} + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: ${{ matrix.license_type.artifact_prefix }}${{ matrix.browser.artifact_name_dev }}-${{ env._BUILD_NUMBER }}.zip + path: browser-source/apps/browser/dist/${{matrix.license_type.archive_name_prefix}}${{ matrix.browser.archive_name_dev }} + if-no-files-found: error + build-safari: name: Build Safari - ${{ matrix.license_type.readable }} - runs-on: macos-13 + runs-on: macos-15 permissions: contents: read id-token: write @@ -369,7 +386,7 @@ jobs: _NODE_VERSION: ${{ needs.setup.outputs.node_version }} steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -506,7 +523,7 @@ jobs: ls -la - name: Upload Safari artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{matrix.license_type.archive_name_prefix}}dist-safari-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/${{matrix.license_type.archive_name_prefix}}dist-safari.zip @@ -525,7 +542,7 @@ jobs: - build-safari steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -548,7 +565,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Upload Sources - uses: crowdin/github-action@f214c8723025f41fc55b2ad26e67b60b80b1885d # v2.7.1 + uses: crowdin/github-action@08713f00a50548bfe39b37e8f44afb53e7a802d4 # v2.12.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index c2abbdf5e5c..964cbc834c5 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -59,7 +59,7 @@ jobs: has_secrets: ${{ steps.check-secrets.outputs.has_secrets }} steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -93,8 +93,8 @@ jobs: [ { base: "linux", distro: "ubuntu-22.04", target_suffix: "" }, { base: "linux", distro: "ubuntu-22.04-arm", target_suffix: "-arm64" }, - { base: "mac", distro: "macos-13", target_suffix: "" }, - { base: "mac", distro: "macos-14", target_suffix: "-arm64" } + { base: "mac", distro: "macos-15-intel", target_suffix: "" }, + { base: "mac", distro: "macos-15", target_suffix: "-arm64" } ] license_type: [ @@ -114,7 +114,7 @@ jobs: steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -268,7 +268,7 @@ jobs: fi - name: Upload unix zip asset - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-${{ env._PACKAGE_VERSION }}.zip @@ -311,7 +311,7 @@ jobs: _WIN_PKG_VERSION: 3.5 steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -482,7 +482,7 @@ jobs: } - name: Upload windows zip asset - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip @@ -490,7 +490,7 @@ jobs: - name: Upload Chocolatey asset if: matrix.license_type.build_prefix == 'bit' - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg path: apps/cli/dist/chocolatey/bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg @@ -503,7 +503,7 @@ jobs: - name: Upload NPM Build Directory asset if: matrix.license_type.build_prefix == 'bit' - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip path: apps/cli/bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip @@ -520,7 +520,7 @@ jobs: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -535,7 +535,7 @@ jobs: echo "BW Package Version: $_PACKAGE_VERSION" - name: Get bw linux cli - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: bw-linux-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/snap @@ -572,7 +572,7 @@ jobs: run: sudo snap remove bw - name: Upload snap asset - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bw_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/cli/dist/snap/bw_${{ env._PACKAGE_VERSION }}_amd64.snap diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 39549c4580c..87ea808a97b 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -55,7 +55,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -88,7 +88,7 @@ jobs: working-directory: apps/desktop steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: true @@ -173,7 +173,7 @@ jobs: working-directory: apps/desktop steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -185,6 +185,13 @@ jobs: cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + with: + workspaces: | + apps/desktop/desktop_native -> target + cache-targets: "true" + - name: Set up environment run: | sudo apt-get update @@ -225,7 +232,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 id: cache with: path: | @@ -244,48 +251,41 @@ jobs: TARGET: musl run: | rustup target add x86_64-unknown-linux-musl - node build.js --target=x86_64-unknown-linux-musl --release + node build.js --target=x86_64-unknown-linux-musl - name: Build application run: npm run dist:lin - name: Upload .deb artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb if-no-files-found: error - name: Upload .rpm artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm if-no-files-found: error - - name: Upload .freebsd artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 - with: - name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd - path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd - if-no-files-found: error - - name: Upload .snap artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap if-no-files-found: error - name: Upload .AppImage artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{ needs.setup.outputs.release_channel }}-linux.yml path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-linux.yml @@ -298,13 +298,12 @@ jobs: sudo npm run pack:lin:flatpak - name: Upload flatpak artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: com.bitwarden.desktop.flatpak path: apps/desktop/dist/com.bitwarden.desktop.flatpak if-no-files-found: error - linux-arm64: name: Linux ARM64 Build # Note, before updating the ubuntu version of the workflow, ensure the snap base image @@ -323,7 +322,7 @@ jobs: working-directory: apps/desktop steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -335,17 +334,34 @@ jobs: cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + with: + workspaces: | + apps/desktop/desktop_native -> target + cache-targets: "true" + - name: Set up environment run: | sudo apt-get update - sudo apt-get -y install pkg-config libxss-dev rpm musl-dev musl-tools flatpak flatpak-builder + sudo apt-get -y install pkg-config libxss-dev rpm musl-dev musl-tools flatpak flatpak-builder squashfs-tools ruby ruby-dev rubygems build-essential + sudo gem install --no-document fpm + + - name: Set up Snap + run: sudo snap install snapcraft --classic + + - name: Install snaps required by snapcraft in destructive mode + run: | + sudo snap install core22 + sudo snap install gtk-common-themes + sudo snap install gnome-3-28-1804 - name: Print environment run: | node --version npm --version snap --version - snapcraft --version || echo 'snapcraft unavailable' + snapcraft --version - name: Install Node dependencies run: npm ci @@ -372,7 +388,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 id: cache with: path: | @@ -391,7 +407,7 @@ jobs: TARGET: musl run: | rustup target add aarch64-unknown-linux-musl - node build.js --target=aarch64-unknown-linux-musl --release + node build.js --target=aarch64-unknown-linux-musl - name: Check index.d.ts generated if: github.event_name == 'pull_request' && steps.cache.outputs.cache-hit != 'true' @@ -403,23 +419,47 @@ jobs: fi - name: Build application + env: + # Snapcraft environment variables to bypass LXD requirement on ARM64 + SNAPCRAFT_BUILD_ENVIRONMENT: host + USE_SYSTEM_FPM: true run: npm run dist:lin:arm64 + - name: Upload .snap artifact + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: bitwarden_${{ env._PACKAGE_VERSION }}_arm64.snap + path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_arm64.snap + if-no-files-found: error + - name: Upload tar.gz artifact - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_arm64.tar.gz path: apps/desktop/dist/bitwarden_desktop_arm64.tar.gz if-no-files-found: error + - name: Build flatpak + working-directory: apps/desktop + run: | + sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo + sudo npm run pack:lin:flatpak + + - name: Upload flatpak artifact + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: com.bitwarden.desktop-arm64.flatpak + path: apps/desktop/dist/com.bitwarden.desktop.flatpak + if-no-files-found: error + windows: name: Windows Build runs-on: windows-2022 needs: - setup permissions: - contents: read - id-token: write + contents: read + id-token: write defaults: run: shell: pwsh @@ -430,7 +470,7 @@ jobs: NODE_OPTIONS: --max_old_space_size=4096 steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -442,6 +482,13 @@ jobs: cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + with: + workspaces: | + apps/desktop/desktop_native -> target + cache-targets: "true" + - name: Install AST run: dotnet tool install --global AzureSignTool --version 4.0.1 @@ -504,7 +551,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 id: cache with: path: | @@ -570,7 +617,7 @@ jobs: -NewName bitwarden-$env:_PACKAGE_VERSION-arm64.nsis.7z - name: Upload portable exe artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe @@ -578,15 +625,15 @@ jobs: - name: Upload installer exe artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: - name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}..exe + name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload appx ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx @@ -594,7 +641,7 @@ jobs: - name: Upload store appx ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx @@ -602,7 +649,7 @@ jobs: - name: Upload NSIS ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z @@ -610,7 +657,7 @@ jobs: - name: Upload appx x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx @@ -618,7 +665,7 @@ jobs: - name: Upload store appx x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx @@ -626,7 +673,7 @@ jobs: - name: Upload NSIS x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z @@ -634,7 +681,7 @@ jobs: - name: Upload appx ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx @@ -642,7 +689,7 @@ jobs: - name: Upload store appx ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx @@ -650,7 +697,7 @@ jobs: - name: Upload NSIS ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z @@ -658,7 +705,7 @@ jobs: - name: Upload nupkg artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden.${{ env._PACKAGE_VERSION }}.nupkg path: apps/desktop/dist/chocolatey/bitwarden.${{ env._PACKAGE_VERSION }}.nupkg @@ -666,7 +713,7 @@ jobs: - name: Upload auto-update artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{ needs.setup.outputs.release_channel }}.yml path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml @@ -677,8 +724,8 @@ jobs: runs-on: windows-2022 needs: setup permissions: - contents: read - id-token: write + contents: read + id-token: write defaults: run: shell: pwsh @@ -689,9 +736,10 @@ jobs: NODE_OPTIONS: --max_old_space_size=4096 steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false - name: Set up Node uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 @@ -700,6 +748,13 @@ jobs: cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + with: + workspaces: | + apps/desktop/desktop_native -> target + cache-targets: "true" + - name: Install AST run: dotnet tool install --global AzureSignTool --version 4.0.1 @@ -759,7 +814,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 id: cache with: path: | @@ -793,25 +848,27 @@ jobs: - name: Rename appx files for store if: ${{ needs.setup.outputs.has_secrets == 'true' }} run: | - Copy-Item "./dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32.appx" ` - -Destination "./dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32-store.appx" - Copy-Item "./dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64.appx" ` - -Destination "./dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64-store.appx" - Copy-Item "./dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64.appx" ` - -Destination "./dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64-store.appx" + Copy-Item "./dist/Bitwarden-Beta-$env:_PACKAGE_VERSION-ia32.appx" ` + -Destination "./dist/Bitwarden-Beta-$env:_PACKAGE_VERSION-ia32-store.appx" + Copy-Item "./dist/Bitwarden-Beta-$env:_PACKAGE_VERSION-x64.appx" ` + -Destination "./dist/Bitwarden-Beta-$env:_PACKAGE_VERSION-x64-store.appx" + Copy-Item "./dist/Bitwarden-Beta-$env:_PACKAGE_VERSION-arm64.appx" ` + -Destination "./dist/Bitwarden-Beta-$env:_PACKAGE_VERSION-arm64-store.appx" - name: Fix NSIS artifact names for auto-updater if: ${{ needs.setup.outputs.has_secrets == 'true' }} run: | - Rename-Item -Path .\dist\nsis-web\Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z ` - -NewName bitwarden-beta-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z - Rename-Item -Path .\dist\nsis-web\Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64.nsis.7z ` - -NewName bitwarden-beta-${{ env._PACKAGE_VERSION }}-x64.nsis.7z - Rename-Item -Path .\dist\nsis-web\Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z ` - -NewName bitwarden-beta-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z + Rename-Item -Path .\dist\nsis-web\Bitwarden-Beta-$env:_PACKAGE_VERSION-ia32.nsis.7z ` + -NewName bitwarden-beta-$env:_PACKAGE_VERSION-ia32.nsis.7z + Rename-Item -Path .\dist\nsis-web\Bitwarden-Beta-$env:_PACKAGE_VERSION-x64.nsis.7z ` + -NewName bitwarden-beta-$env:_PACKAGE_VERSION-x64.nsis.7z + Rename-Item -Path .\dist\nsis-web\Bitwarden-Beta-$env:_PACKAGE_VERSION-arm64.nsis.7z ` + -NewName bitwarden-beta-$env:_PACKAGE_VERSION-arm64.nsis.7z + Rename-Item -Path .\dist\nsis-web\latest.yml ` + -NewName latest-beta.yml - name: Upload portable exe artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-Portable-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/Bitwarden-Beta-Portable-${{ env._PACKAGE_VERSION }}.exe @@ -819,7 +876,7 @@ jobs: - name: Upload installer exe artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-Installer-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/nsis-web/Bitwarden-Beta-Installer-${{ env._PACKAGE_VERSION }}.exe @@ -827,7 +884,7 @@ jobs: - name: Upload appx ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32.appx @@ -835,7 +892,7 @@ jobs: - name: Upload store appx ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32-store.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32-store.appx @@ -843,7 +900,7 @@ jobs: - name: Upload NSIS ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-beta-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-beta-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z @@ -851,7 +908,7 @@ jobs: - name: Upload appx x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64.appx @@ -859,7 +916,7 @@ jobs: - name: Upload store appx x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64-store.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64-store.appx @@ -867,7 +924,7 @@ jobs: - name: Upload NSIS x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-beta-${{ env._PACKAGE_VERSION }}-x64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-beta-${{ env._PACKAGE_VERSION }}-x64.nsis.7z @@ -875,7 +932,7 @@ jobs: - name: Upload appx ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64.appx @@ -883,7 +940,7 @@ jobs: - name: Upload store appx ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64-store.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64-store.appx @@ -891,7 +948,7 @@ jobs: - name: Upload NSIS ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-beta-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-beta-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z @@ -899,21 +956,20 @@ jobs: - name: Upload auto-update artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: - name: ${{ needs.setup.outputs.release_channel }}-beta.yml - path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml + name: latest-beta.yml + path: apps/desktop/dist/nsis-web/latest-beta.yml if-no-files-found: error - macos-build: name: MacOS Build - runs-on: macos-13 + runs-on: macos-15 needs: - setup permissions: - contents: read - id-token: write + contents: read + id-token: write env: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} @@ -923,7 +979,7 @@ jobs: working-directory: apps/desktop steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -935,9 +991,21 @@ jobs: cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} + - name: Set up Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + with: + python-version: '3.14' + - name: Set up Node-gyp run: python3 -m pip install setuptools + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + with: + workspaces: | + apps/desktop/desktop_native -> target + cache-targets: "true" + - name: Print environment run: | node --version @@ -948,14 +1016,14 @@ jobs: - name: Cache Build id: build-cache - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Cache Safari id: safari-cache - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -1101,7 +1169,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 id: cache with: path: | @@ -1117,7 +1185,6 @@ jobs: - name: Build application (dev) run: npm run build - browser-build: name: Browser Build needs: setup @@ -1129,18 +1196,17 @@ jobs: pull-requests: write id-token: write - macos-package-github: name: MacOS Package GitHub Release Assets - runs-on: macos-13 + runs-on: macos-15 if: ${{ needs.setup.outputs.has_secrets == 'true' }} needs: - browser-build - macos-build - setup permissions: - contents: read - id-token: write + contents: read + id-token: write env: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} @@ -1150,7 +1216,7 @@ jobs: working-directory: apps/desktop steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -1162,9 +1228,21 @@ jobs: cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} + - name: Set up Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + with: + python-version: '3.14' + - name: Set up Node-gyp run: python3 -m pip install setuptools + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + with: + workspaces: | + apps/desktop/desktop_native -> target + cache-targets: "true" + - name: Print environment run: | node --version @@ -1175,14 +1253,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -1312,7 +1390,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 id: cache with: path: | @@ -1330,7 +1408,7 @@ jobs: run: npm run build - name: Download Browser artifact - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: path: ${{ github.workspace }}/browser-build-artifacts @@ -1363,45 +1441,44 @@ jobs: run: npm run pack:mac - name: Upload .zip artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip if-no-files-found: error - name: Upload .dmg artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg if-no-files-found: error - name: Upload .dmg blockmap artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{ needs.setup.outputs.release_channel }}-mac.yml path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-mac.yml if-no-files-found: error - macos-package-mas: name: MacOS Package Prod Release Asset - runs-on: macos-13 + runs-on: macos-15 if: ${{ needs.setup.outputs.has_secrets == 'true' }} needs: - browser-build - macos-build - setup permissions: - contents: read - id-token: write + contents: read + id-token: write env: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} @@ -1411,7 +1488,7 @@ jobs: working-directory: apps/desktop steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -1423,9 +1500,21 @@ jobs: cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} + - name: Set up Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + with: + python-version: '3.14' + - name: Set up Node-gyp run: python3 -m pip install setuptools + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + with: + workspaces: | + apps/desktop/desktop_native -> target + cache-targets: "true" + - name: Print environment run: | node --version @@ -1436,14 +1525,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -1581,7 +1670,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 id: cache with: path: | @@ -1599,7 +1688,7 @@ jobs: run: npm run build - name: Download Browser artifact - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: path: ${{ github.workspace }}/browser-build-artifacts @@ -1642,14 +1731,14 @@ jobs: $buildInfo | ConvertTo-Json | Set-Content -Path dist/macos-build-number.json - name: Upload MacOS App Store build number artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: macos-build-number.json path: apps/desktop/dist/macos-build-number.json if-no-files-found: error - name: Upload .pkg artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg @@ -1702,7 +1791,7 @@ jobs: if: | 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@485a9d42d3a73031f12ec201c457e2162c45d02d # v2.0.0 + uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1 with: channel-id: C074F5UESQ0 method: chat.postMessage @@ -1731,13 +1820,13 @@ jobs: - macos-package-github - macos-package-mas permissions: - contents: write - pull-requests: write - id-token: write + contents: write + pull-requests: write + id-token: write runs-on: ubuntu-22.04 steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -1760,7 +1849,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Upload Sources - uses: crowdin/github-action@f214c8723025f41fc55b2ad26e67b60b80b1885d # v2.7.1 + uses: crowdin/github-action@08713f00a50548bfe39b37e8f44afb53e7a802d4 # v2.12.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} @@ -1771,7 +1860,6 @@ jobs: upload_sources: true upload_translations: false - check-failures: name: Check for failures if: always() @@ -1787,8 +1875,8 @@ jobs: - macos-package-mas - crowdin-push permissions: - contents: read - id-token: write + contents: read + id-token: write steps: - name: Check if any job failed if: | @@ -1823,4 +1911,3 @@ jobs: SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} with: status: ${{ job.status }} - diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 0ea3ad7af78..02ab7727c24 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -64,7 +64,7 @@ jobs: has_secrets: ${{ steps.check-secrets.outputs.has_secrets }} steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -144,7 +144,7 @@ jobs: _VERSION: ${{ needs.setup.outputs.version }} steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -174,7 +174,7 @@ jobs: echo "server_ref=$SERVER_REF" >> "$GITHUB_OUTPUT" - name: Check out Server repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: path: server repository: bitwarden/server @@ -204,7 +204,7 @@ jobs: ########## Set up Docker ########## - name: Set up Docker - uses: docker/setup-docker-action@b60f85385d03ac8acfca6d9996982511d8620a19 # v4.3.0 + uses: docker/setup-docker-action@efe9e3891a4f7307e689f2100b33a155b900a608 # v4.5.0 with: daemon-config: | { @@ -215,10 +215,10 @@ jobs: } - name: Set up QEMU emulators - uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 ########## ACRs ########## - name: Log in to Azure @@ -273,7 +273,7 @@ jobs: - name: Build Docker image id: build-container - uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: build-args: | NODE_VERSION=${{ env._NODE_VERSION }} @@ -307,7 +307,7 @@ jobs: zip -r web-$_VERSION-${{ matrix.artifact_name }}.zip build - name: Upload ${{ matrix.artifact_name }} artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip path: apps/web/web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip @@ -315,7 +315,7 @@ jobs: - name: Install Cosign if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main' - uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2 + uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 - name: Sign image with Cosign if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main' @@ -334,7 +334,7 @@ jobs: - name: Scan Docker image if: ${{ needs.setup.outputs.has_secrets == 'true' }} id: container-scan - uses: anchore/scan-action@2c901ab7378897c01b8efaa2d0c9bf519cc64b9e # v6.2.0 + uses: anchore/scan-action@568b89d27fc18c60e56937bff480c91c772cd993 # v7.1.0 with: image: ${{ steps.image-name.outputs.name }} fail-build: false @@ -367,7 +367,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -390,7 +390,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Upload Sources - uses: crowdin/github-action@f214c8723025f41fc55b2ad26e67b60b80b1885d # v2.7.1 + uses: crowdin/github-action@08713f00a50548bfe39b37e8f44afb53e7a802d4 # v2.12.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index ccac9cb32bb..677d3dfc1df 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -31,7 +31,7 @@ jobs: steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 @@ -65,7 +65,7 @@ jobs: - name: Cache NPM id: npm-cache - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: "~/.npm" key: ${{ runner.os }}-npm-chromatic-${{ hashFiles('**/package-lock.json') }} @@ -98,7 +98,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Publish to Chromatic - uses: chromaui/action@d0795df816d05c4a89c80295303970fddd247cce # v13.1.4 + uses: chromaui/action@ac86f2ff0a458ffbce7b40698abd44c0fa34d4b6 # v13.3.3 with: token: ${{ secrets.GITHUB_TOKEN }} projectToken: ${{ steps.get-kv-secrets.outputs.CHROMATIC-PROJECT-TOKEN }} diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index f195afa86da..5475c4dd692 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -49,14 +49,16 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Generate GH App token - uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3 + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 id: app-token with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + permission-contents: write # for creating, committing to, and pushing new branches + permission-pull-requests: write # for generating pull requests - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: token: ${{ steps.app-token.outputs.token }} persist-credentials: false diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml index 5aa0918048b..1deeea12f88 100644 --- a/.github/workflows/deploy-web.yml +++ b/.github/workflows/deploy-web.yml @@ -54,8 +54,7 @@ on: type: string required: false -permissions: - deployments: write +permissions: {} jobs: setup: @@ -373,10 +372,16 @@ jobs: - name: Login to Azure uses: bitwarden/gh-actions/azure-login@main + env: + # The following 2 values are ignored in Zizmor, because they have to be dynamically mapped from secrets + # The only way around this is to create separate steps per environment with static secret references, which is not maintainable + SUBSCRIPTION_ID: ${{ secrets[ needs.setup.outputs.azure_login_subscription_id_key_name ] }} # zizmor: ignore[overprovisioned-secrets] + CLIENT_ID: ${{ secrets[ needs.setup.outputs.azure_login_client_key_name ] }} # zizmor: ignore[overprovisioned-secrets] + TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} with: - subscription_id: ${{ secrets[needs.setup.outputs.azure_login_subscription_id_key_name] }} - tenant_id: ${{ secrets.AZURE_TENANT_ID }} - client_id: ${{ secrets[needs.setup.outputs.azure_login_client_key_name] }} + subscription_id: ${{ env.SUBSCRIPTION_ID }} + tenant_id: ${{ env.TENANT_ID }} + client_id: ${{ env.CLIENT_ID }} - name: Retrieve Storage Account name id: retrieve-secrets-azcopy diff --git a/.github/workflows/lint-crowdin-config.yml b/.github/workflows/lint-crowdin-config.yml index ee22a03963c..b0efeb50823 100644 --- a/.github/workflows/lint-crowdin-config.yml +++ b/.github/workflows/lint-crowdin-config.yml @@ -22,7 +22,7 @@ jobs: ] steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: fetch-depth: 1 persist-credentials: false @@ -45,7 +45,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Lint ${{ matrix.app.name }} config - uses: crowdin/github-action@f214c8723025f41fc55b2ad26e67b60b80b1885d # v2.7.1 + uses: crowdin/github-action@08713f00a50548bfe39b37e8f44afb53e7a802d4 # v2.12.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_PROJECT_ID: ${{ matrix.app.project_id }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c14abd7cd86..48d3eca2f4e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -94,16 +94,31 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false + - name: Install Rust + uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # stable + with: + toolchain: stable + components: rustfmt, clippy + + - name: Install Rust nightly + uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # stable + with: + toolchain: nightly + components: rustfmt + - name: Check Rust version run: rustup --version + - name: Cache cargo registry + uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7 + - name: Run cargo fmt working-directory: ./apps/desktop/desktop_native - run: cargo fmt --check + run: cargo +nightly fmt --check - name: Run Clippy working-directory: ./apps/desktop/desktop_native @@ -118,6 +133,13 @@ jobs: working-directory: ./apps/desktop/desktop_native run: cargo sort --workspace --check + - name: Install cargo-udeps + run: cargo install cargo-udeps --version 0.1.57 --locked + + - name: Cargo udeps + working-directory: ./apps/desktop/desktop_native + run: cargo +nightly udeps --workspace --all-features --all-targets + - name: Install cargo-deny uses: taiki-e/install-action@81ee1d48d9194cdcab880cbdc7d36e87d39874cb # v2.62.45 with: diff --git a/.github/workflows/locales-lint.yml b/.github/workflows/locales-lint.yml index da79f9aa21f..8335d6aacad 100644 --- a/.github/workflows/locales-lint.yml +++ b/.github/workflows/locales-lint.yml @@ -17,11 +17,11 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false - name: Checkout base branch repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ github.event.pull_request.base.sha }} path: base diff --git a/.github/workflows/nx.yml b/.github/workflows/nx.yml index 43361bc983d..0f01aa27899 100644 --- a/.github/workflows/nx.yml +++ b/.github/workflows/nx.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: fetch-depth: 0 persist-credentials: false diff --git a/.github/workflows/publish-cli.yml b/.github/workflows/publish-cli.yml index bcae79d077e..8fcd1fe7c98 100644 --- a/.github/workflows/publish-cli.yml +++ b/.github/workflows/publish-cli.yml @@ -66,15 +66,17 @@ jobs: - name: Version output id: version-output env: - _INPUT_VERSION: ${{ inputs.version }} + INPUT_VERSION: ${{ inputs.version }} run: | - if [[ "$_INPUT_VERSION" == "latest" || "$_INPUT_VERSION" == "" ]]; then - VERSION=$(curl "https://api.github.com/repos/bitwarden/clients/releases" | jq -c '.[] | select(.tag_name | contains("cli")) | .tag_name' | head -1 | grep -ohE '20[0-9]{2}\.([1-9]|1[0-2])\.[0-9]+') + if [[ "$INPUT_VERSION" == "latest" || "$INPUT_VERSION" == "" ]]; then + TAG_NAME=$(curl -s "https://api.github.com/repos/bitwarden/clients/releases" \ + | jq -r '.[] | select(.tag_name | contains("cli")) | .tag_name' | head -1) + VERSION="${TAG_NAME#cli-v}" echo "Latest Released Version: $VERSION" echo "version=$VERSION" >> "$GITHUB_OUTPUT" else - echo "Release Version: $_INPUT_VERSION" - echo "version=$_INPUT_VERSION" >> "$GITHUB_OUTPUT" + echo "Release Version: $INPUT_VERSION" + echo "version=$INPUT_VERSION" >> "$GITHUB_OUTPUT" fi - name: Create GitHub deployment @@ -101,7 +103,7 @@ jobs: _PKG_VERSION: ${{ needs.setup.outputs.release_version }} steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -126,14 +128,14 @@ jobs: uses: samuelmeuli/action-snapcraft@fceeb3c308e76f3487e72ef608618de625fb7fe8 # v3.0.1 - name: Download artifacts - run: wget "https://github.com/bitwarden/clients/releases/download/cli-v$_PKG_VERSION/bw_$_PKG_VERSION_amd64.snap" + run: wget "https://github.com/bitwarden/clients/releases/download/cli-v${_PKG_VERSION}/bw_${_PKG_VERSION}_amd64.snap" - name: Publish Snap & logout if: ${{ inputs.publish_type != 'Dry Run' }} env: SNAPCRAFT_STORE_CREDENTIALS: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }} run: | - snapcraft upload "bw_$_PKG_VERSION_amd64.snap" --release stable + snapcraft upload "bw_${_PKG_VERSION}_amd64.snap" --release stable snapcraft logout choco: @@ -149,7 +151,7 @@ jobs: _PKG_VERSION: ${{ needs.setup.outputs.release_version }} steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -179,7 +181,7 @@ jobs: run: New-Item -ItemType directory -Path ./dist - name: Download artifacts - run: Invoke-WebRequest -Uri "https://github.com/bitwarden/clients/releases/download/cli-v$_PKG_VERSION/bitwarden-cli.$_PKG_VERSION.nupkg" -OutFile bitwarden-cli.$_PKG_VERSION.nupkg + run: Invoke-WebRequest -Uri "https://github.com/bitwarden/clients/releases/download/cli-v$($env:_PKG_VERSION)/bitwarden-cli.$($env:_PKG_VERSION).nupkg" -OutFile bitwarden-cli.$($env:_PKG_VERSION).nupkg working-directory: apps/cli/dist - name: Push to Chocolatey @@ -201,10 +203,10 @@ jobs: _PKG_VERSION: ${{ needs.setup.outputs.release_version }} steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false - + - name: Get Node version id: retrieve-node-version working-directory: ./ @@ -227,8 +229,8 @@ jobs: - name: Download and set up artifact run: | mkdir -p build - wget "https://github.com/bitwarden/clients/releases/download/cli-v$_PKG_VERSION/bitwarden-cli-$_PKG_VERSION-npm-build.zip" - unzip "bitwarden-cli-$_PKG_VERSION-npm-build.zip" -d build + wget "https://github.com/bitwarden/clients/releases/download/cli-v${_PKG_VERSION}/bitwarden-cli-${_PKG_VERSION}-npm-build.zip" + unzip "bitwarden-cli-${_PKG_VERSION}-npm-build.zip" -d build - name: Publish NPM if: ${{ inputs.publish_type != 'Dry Run' }} diff --git a/.github/workflows/publish-desktop.yml b/.github/workflows/publish-desktop.yml index 2e9ba635e7a..3d512d49559 100644 --- a/.github/workflows/publish-desktop.yml +++ b/.github/workflows/publish-desktop.yml @@ -73,12 +73,11 @@ jobs: - name: Check Publish Version id: version env: - _INPUT_VERSION: ${{ inputs.version }} + INPUT_VERSION: ${{ inputs.version }} run: | - if [[ "$_INPUT_VERSION" == "latest" || "$_INPUT_VERSION" == "" ]]; then - TAG_NAME=$(curl "https://api.github.com/repos/bitwarden/clients/releases" \ - | jq -c '.[] | select(.tag_name | contains("desktop")) | .tag_name' \ - | head -1 | cut -d '"' -f 2) + if [[ "$INPUT_VERSION" == "latest" || "$INPUT_VERSION" == "" ]]; then + TAG_NAME=$(curl -s "https://api.github.com/repos/bitwarden/clients/releases" \ + | jq -r '.[] | select(.tag_name | contains("desktop")) | .tag_name' | head -1) VERSION="${TAG_NAME#desktop-v}" echo "Latest Released Version: $VERSION" @@ -87,7 +86,7 @@ jobs: echo "Tag name: $TAG_NAME" echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT" else - VERSION="$_INPUT_VERSION" + VERSION="$INPUT_VERSION" TAG_NAME="desktop-v$VERSION" echo "Release Version: $VERSION" @@ -100,9 +99,9 @@ jobs: - name: Get Version Channel id: release_channel env: - _VERSION: ${{ steps.version.outputs.version }} + VERSION: ${{ steps.version.outputs.version }} run: | - case "${_VERSION}" in + case "${VERSION}" in *"alpha"*) echo "channel=alpha" >> "$GITHUB_OUTPUT" echo "[!] We do not yet support 'alpha'" @@ -192,22 +191,6 @@ jobs: --recursive \ --quiet - - name: Update deployment status to Success - if: ${{ inputs.publish_type != 'Dry Run' && success() }} - uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3 - with: - token: '${{ secrets.GITHUB_TOKEN }}' - state: 'success' - deployment-id: ${{ needs.setup.outputs.deployment_id }} - - - name: Update deployment status to Failure - if: ${{ inputs.publish_type != 'Dry Run' && failure() }} - uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3 - with: - token: '${{ secrets.GITHUB_TOKEN }}' - state: 'failure' - deployment-id: ${{ needs.setup.outputs.deployment_id }} - snap: name: Deploy Snap runs-on: ubuntu-22.04 @@ -221,7 +204,7 @@ jobs: _RELEASE_TAG: ${{ needs.setup.outputs.tag_name }} steps: - name: Checkout Repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -251,14 +234,14 @@ jobs: - name: Download artifacts working-directory: apps/desktop/dist - run: wget "https://github.com/bitwarden/clients/releases/download/$_RELEASE_TAG/bitwarden_$_PKG_VERSION_amd64.snap" + run: wget "https://github.com/bitwarden/clients/releases/download/${_RELEASE_TAG}/bitwarden_${_PKG_VERSION}_amd64.snap" - name: Deploy to Snap Store if: ${{ inputs.publish_type != 'Dry Run' }} env: SNAPCRAFT_STORE_CREDENTIALS: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }} run: | - snapcraft upload "bitwarden_$_PKG_VERSION_amd64.snap" --release stable + snapcraft upload "bitwarden_${_PKG_VERSION}_amd64.snap" --release stable snapcraft logout working-directory: apps/desktop/dist @@ -275,7 +258,7 @@ jobs: _RELEASE_TAG: ${{ needs.setup.outputs.tag_name }} steps: - name: Checkout Repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -312,7 +295,7 @@ jobs: - name: Download artifacts working-directory: apps/desktop/dist - run: Invoke-WebRequest -Uri "https://github.com/bitwarden/clients/releases/download/$_RELEASE_TAG/bitwarden.$_PKG_VERSION.nupkg" -OutFile "bitwarden.$_PKG_VERSION.nupkg" + run: Invoke-WebRequest -Uri "https://github.com/bitwarden/clients/releases/download/$($env:_RELEASE_TAG)/bitwarden.$($env:_PKG_VERSION).nupkg" -OutFile "bitwarden.$($env:_PKG_VERSION).nupkg" - name: Push to Chocolatey if: ${{ inputs.publish_type != 'Dry Run' }} @@ -332,12 +315,12 @@ jobs: _RELEASE_TAG: ${{ needs.setup.outputs.tag_name }} steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false - name: Validate release notes for MAS - if: inputs.mas_publish && (inputs.release_notes == '' || inputs.release_notes == null) + if: inputs.release_notes == '' || inputs.release_notes == null run: | echo "❌ Release notes are required when publishing to Mac App Store" echo "Please provide release notes using the 'Release Notes' input field" @@ -345,15 +328,15 @@ jobs: - name: Download MacOS App Store build number working-directory: apps/desktop - run: wget "https://github.com/bitwarden/clients/releases/download/$_RELEASE_TAG/macos-build-number.json" + run: wget "https://github.com/bitwarden/clients/releases/download/${_RELEASE_TAG}/macos-build-number.json" - name: Setup Ruby and Install Fastlane - uses: ruby/setup-ruby@ca041f971d66735f3e5ff1e21cc13e2d51e7e535 # v1.233.0 + uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0 with: - ruby-version: '3.0' + ruby-version: '3.4.7' bundler-cache: false working-directory: apps/desktop - + - name: Install Fastlane working-directory: apps/desktop run: gem install fastlane @@ -379,32 +362,32 @@ jobs: env: APP_STORE_CONNECT_TEAM_ISSUER: ${{ steps.get-kv-secrets.outputs.APP-STORE-CONNECT-TEAM-ISSUER }} APP_STORE_CONNECT_AUTH_KEY: ${{ steps.get-kv-secrets.outputs.APP-STORE-CONNECT-AUTH-KEY }} - _RELEASE_NOTES: ${{ inputs.release_notes }} - _PUBLISH_TYPE: ${{ inputs.publish_type }} + CHANGELOG: ${{ inputs.release_notes }} + PUBLISH_TYPE: ${{ inputs.publish_type }} working-directory: apps/desktop run: | BUILD_NUMBER=$(jq -r '.buildNumber' macos-build-number.json) - CHANGELOG="$_RELEASE_NOTES" - IS_DRY_RUN="$_PUBLISH_TYPE == 'Dry Run'" - if [ "$IS_DRY_RUN" = "true" ]; then + if [ "$PUBLISH_TYPE" = "Dry Run" ]; then echo "🧪 DRY RUN MODE - Testing without actual App Store submission" echo "📦 Would publish build $BUILD_NUMBER to Mac App Store" + IS_DRY_RUN="true" else echo "🚀 PRODUCTION MODE - Publishing to Mac App Store" echo "📦 Publishing build $BUILD_NUMBER to Mac App Store" + IS_DRY_RUN="false" fi - + echo "📝 Release notes (${#CHANGELOG} chars): ${CHANGELOG:0:100}..." - + # Validate changelog length (App Store limit is 4000 chars) if [ ${#CHANGELOG} -gt 4000 ]; then echo "❌ Release notes too long: ${#CHANGELOG} characters (max 4000)" exit 1 fi - + fastlane publish --verbose \ - app_version:"$PKG_VERSION" \ + app_version:"${_PKG_VERSION}" \ build_number:"$BUILD_NUMBER" \ changelog:"$CHANGELOG" \ dry_run:"$IS_DRY_RUN" diff --git a/.github/workflows/publish-web.yml b/.github/workflows/publish-web.yml index 6bf2b282b38..be93ee61479 100644 --- a/.github/workflows/publish-web.yml +++ b/.github/workflows/publish-web.yml @@ -28,7 +28,7 @@ jobs: contents: read steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -74,7 +74,7 @@ jobs: echo "Github Release Option: $_RELEASE_OPTION" - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -157,11 +157,10 @@ jobs: - name: Log out of Docker run: docker logout - self-host-unified-build: - name: Trigger self-host unified build + bitwarden-lite-build: + name: Trigger Bitwarden Lite build runs-on: ubuntu-22.04 - needs: - - setup + needs: setup permissions: id-token: write steps: @@ -182,7 +181,7 @@ jobs: - name: Log out from Azure uses: bitwarden/gh-actions/azure-logout@main - - name: Trigger self-host build + - name: Trigger Bitwarden Lite build uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }} @@ -190,7 +189,7 @@ jobs: await github.rest.actions.createWorkflowDispatch({ owner: 'bitwarden', repo: 'self-host', - workflow_id: 'build-unified.yml', + workflow_id: 'build-bitwarden-lite.yml', ref: 'main', inputs: { use_latest_core_version: true diff --git a/.github/workflows/release-browser.yml b/.github/workflows/release-browser.yml index 39f54a6e2db..ff5fb669faf 100644 --- a/.github/workflows/release-browser.yml +++ b/.github/workflows/release-browser.yml @@ -28,7 +28,7 @@ jobs: release_version: ${{ steps.version.outputs.version }} steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -61,7 +61,7 @@ jobs: contents: read steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -132,15 +132,15 @@ jobs: env: PACKAGE_VERSION: ${{ needs.setup.outputs.release_version }} run: | - mv browser-source.zip "browser-source-$PACKAGE_VERSION.zip" - mv dist-chrome.zip "dist-chrome-$PACKAGE_VERSION.zip" - mv dist-opera.zip "dist-opera-$PACKAGE_VERSION.zip" - mv dist-firefox.zip "dist-firefox-$PACKAGE_VERSION.zip" - mv dist-edge.zip "dist-edge-$PACKAGE_VERSION.zip" + mv browser-source.zip "browser-source-${PACKAGE_VERSION}.zip" + mv dist-chrome.zip "dist-chrome-${PACKAGE_VERSION}.zip" + mv dist-opera.zip "dist-opera-${PACKAGE_VERSION}.zip" + mv dist-firefox.zip "dist-firefox-${PACKAGE_VERSION}.zip" + mv dist-edge.zip "dist-edge-${PACKAGE_VERSION}.zip" - name: Create release if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: ncipollo/release-action@cdcc88a9acf3ca41c16c37bb7d21b9ad48560d87 # v1.15.0 + uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0 with: artifacts: 'browser-source-${{ needs.setup.outputs.release_version }}.zip, dist-chrome-${{ needs.setup.outputs.release_version }}.zip, diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index d5013770476..08045b8d3c7 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -29,7 +29,7 @@ jobs: release_version: ${{ steps.version.outputs.version }} steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -80,7 +80,7 @@ jobs: - name: Create release if: ${{ inputs.release_type != 'Dry Run' }} - uses: ncipollo/release-action@cdcc88a9acf3ca41c16c37bb7d21b9ad48560d87 # v1.15.0 + uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0 env: PKG_VERSION: ${{ needs.setup.outputs.release_version }} with: diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 9239914aeff..c6b671ee5ea 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -31,7 +31,7 @@ jobs: release_channel: ${{ steps.release_channel.outputs.channel }} steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -58,9 +58,9 @@ jobs: - name: Get Version Channel id: release_channel env: - _VERSION: ${{ steps.version.outputs.version }} + VERSION: ${{ steps.version.outputs.version }} run: | - case "$_VERSION" in + case "$VERSION" in *"alpha"*) echo "channel=alpha" >> "$GITHUB_OUTPUT" echo "[!] We do not yet support 'alpha'" @@ -96,10 +96,10 @@ jobs: env: PKG_VERSION: ${{ steps.version.outputs.version }} working-directory: apps/desktop/artifacts - run: mv "Bitwarden-$PKG_VERSION-universal.pkg" "Bitwarden-$PKG_VERSION-universal.pkg.archive" + run: mv "Bitwarden-${PKG_VERSION}-universal.pkg" "Bitwarden-${PKG_VERSION}-universal.pkg.archive" - name: Create Release - uses: ncipollo/release-action@cdcc88a9acf3ca41c16c37bb7d21b9ad48560d87 # v1.15.0 + uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0 if: ${{ steps.release_channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' }} env: PKG_VERSION: ${{ steps.version.outputs.version }} @@ -107,8 +107,9 @@ jobs: with: artifacts: "apps/desktop/artifacts/Bitwarden-${{ env.PKG_VERSION }}-amd64.deb, apps/desktop/artifacts/Bitwarden-${{ env.PKG_VERSION }}-x86_64.rpm, - apps/desktop/artifacts/Bitwarden-${{ env.PKG_VERSION }}-x64.freebsd, apps/desktop/artifacts/bitwarden_${{ env.PKG_VERSION }}_amd64.snap, + apps/desktop/artifacts/bitwarden_${{ env.PKG_VERSION }}_arm64.snap, + apps/desktop/artifacts/bitwarden_${{ env.PKG_VERSION }}_arm64.tar.gz, apps/desktop/artifacts/Bitwarden-${{ env.PKG_VERSION }}-x86_64.AppImage, apps/desktop/artifacts/Bitwarden-Portable-${{ env.PKG_VERSION }}.exe, apps/desktop/artifacts/Bitwarden-Installer-${{ env.PKG_VERSION }}.exe, diff --git a/.github/workflows/release-web.yml b/.github/workflows/release-web.yml index 8c8f8ed86af..fc0ac340234 100644 --- a/.github/workflows/release-web.yml +++ b/.github/workflows/release-web.yml @@ -25,7 +25,7 @@ jobs: tag_version: ${{ steps.version.outputs.tag }} steps: - name: Checkout repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -52,8 +52,7 @@ jobs: release: name: Create GitHub Release runs-on: ubuntu-22.04 - needs: - - setup + needs: setup permissions: contents: write steps: @@ -82,14 +81,14 @@ jobs: - name: Rename assets working-directory: apps/web/artifacts env: - _RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} + RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} run: | - mv web-*-selfhosted-COMMERCIAL.zip "web-$_RELEASE_VERSION-selfhosted-COMMERCIAL.zip" - mv web-*-selfhosted-open-source.zip "web-$_RELEASE_VERSION-selfhosted-open-source.zip" + mv web-*-selfhosted-COMMERCIAL.zip "web-${RELEASE_VERSION}-selfhosted-COMMERCIAL.zip" + mv web-*-selfhosted-open-source.zip "web-${RELEASE_VERSION}-selfhosted-open-source.zip" - name: Create release if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: ncipollo/release-action@cdcc88a9acf3ca41c16c37bb7d21b9ad48560d87 # v1.15.0 + uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0 with: name: "Web v${{ needs.setup.outputs.release_version }}" commit: ${{ github.sha }} diff --git a/.github/workflows/repository-management.yml b/.github/workflows/repository-management.yml index ce9b70118b2..faf119cce2b 100644 --- a/.github/workflows/repository-management.yml +++ b/.github/workflows/repository-management.yml @@ -97,14 +97,14 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Generate GH App token - uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3 + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 id: app-token with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} - name: Check out branch - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: main token: ${{ steps.app-token.outputs.token }} @@ -462,14 +462,14 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Generate GH App token - uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3 + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 id: app-token with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} - name: Check out target ref - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: ${{ inputs.target_ref }} token: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/review-code.yml b/.github/workflows/review-code.yml index 46309af38ea..0e0597fccf0 100644 --- a/.github/workflows/review-code.yml +++ b/.github/workflows/review-code.yml @@ -15,6 +15,7 @@ jobs: AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} permissions: + actions: read contents: read id-token: write pull-requests: write diff --git a/.github/workflows/sdk-breaking-change-check.yml b/.github/workflows/sdk-breaking-change-check.yml index 49b91d2d1a1..14547b3942f 100644 --- a/.github/workflows/sdk-breaking-change-check.yml +++ b/.github/workflows/sdk-breaking-change-check.yml @@ -1,10 +1,26 @@ # This workflow runs TypeScript compatibility checks when the SDK is updated. -# Triggered automatically by the SDK repository via repository_dispatch when SDK PRs are created/updated. +# Triggered automatically by the SDK repository via workflow_dispatch when SDK PRs are created/updated. name: SDK Breaking Change Check -run-name: "SDK breaking change check (${{ github.event.client_payload.sdk_version }})" +run-name: "SDK breaking change check (${{ github.event.inputs.sdk_version }})" on: - repository_dispatch: - types: [sdk-breaking-change-check] + workflow_dispatch: + inputs: + sdk_version: + description: "SDK version being tested" + required: true + type: string + source_repo: + description: "Source repository" + required: true + type: string + artifacts_run_id: + description: "Artifacts run ID" + required: true + type: string + artifact_name: + description: "Artifact name" + required: true + type: string permissions: contents: read @@ -17,12 +33,11 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 15 env: - _SOURCE_REPO: ${{ github.event.client_payload.source_repo }} - _SDK_VERSION: ${{ github.event.client_payload.sdk_version }} - _ARTIFACTS_RUN_ID: ${{ github.event.client_payload.artifacts_info.run_id }} - _ARTIFACT_NAME: ${{ github.event.client_payload.artifacts_info.artifact_name }} - _CLIENT_LABEL: ${{ github.event.client_payload.client_label }} - + _SOURCE_REPO: ${{ github.event.inputs.source_repo }} + _SDK_VERSION: ${{ github.event.inputs.sdk_version }} + _ARTIFACTS_RUN_ID: ${{ github.event.inputs.artifacts_run_id }} + _ARTIFACT_NAME: ${{ github.event.inputs.artifact_name }} + steps: - name: Log in to Azure uses: bitwarden/gh-actions/azure-login@main @@ -38,30 +53,18 @@ jobs: secrets: "BW-GHAPP-ID,BW-GHAPP-KEY" - name: Generate GH App token - uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3 + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 id: app-token with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + permission-actions: read # for reading and downloading the artifacts for a workflow run + - name: Log out from Azure uses: bitwarden/gh-actions/azure-logout@main - - name: Validate inputs - run: | - echo "🔍 Validating required client_payload fields..." - if [ -z "${_SOURCE_REPO}" ] || [ -z "${_SDK_VERSION}" ] || [ -z "${_ARTIFACTS_RUN_ID}" ] || [ -z "${_ARTIFACT_NAME}" ]; then - echo "::error::Missing required client_payload fields" - echo "SOURCE_REPO: ${_SOURCE_REPO}" - echo "SDK_VERSION: ${_SDK_VERSION}" - echo "ARTIFACTS_RUN_ID: ${_ARTIFACTS_RUN_ID}" - echo "ARTIFACT_NAME: ${_ARTIFACT_NAME}" - echo "CLIENT_LABEL: ${_CLIENT_LABEL}" - exit 1 - fi - - echo "✅ All required payload fields are present" - name: Check out clients repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -134,34 +137,30 @@ jobs: - name: Run TypeScript compatibility check run: | - echo "🔍 Running TypeScript type checking for ${_CLIENT_LABEL} client with SDK version: ${_SDK_VERSION}" + echo "🔍 Running TypeScript type checking with SDK version: ${_SDK_VERSION}" echo "🎯 Type checking command: npm run test:types" # Add GitHub Step Summary output - { - echo "## 📊 TypeScript Compatibility Check (${_CLIENT_LABEL})" - echo "- **Client**: ${_CLIENT_LABEL}" - echo "- **SDK Version**: ${_SDK_VERSION}" - echo "- **Source Repository**: ${_SOURCE_REPO}" - echo "- **Artifacts Run ID**: ${_ARTIFACTS_RUN_ID}" - echo "" - } >> "$GITHUB_STEP_SUMMARY" - - + echo "## 📊 TypeScript Compatibility Check" >> $GITHUB_STEP_SUMMARY + echo "- **SDK Version**: ${_SDK_VERSION}" >> $GITHUB_STEP_SUMMARY + echo "- **Source Repository**: ${_SOURCE_REPO}" >> $GITHUB_STEP_SUMMARY + echo "- **Artifacts Run ID**: ${_ARTIFACTS_RUN_ID}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + TYPE_CHECK_START=$(date +%s) # Run type check with timeout - exit code determines gh run watch result if timeout 10m npm run test:types; then TYPE_CHECK_END=$(date +%s) TYPE_CHECK_DURATION=$((TYPE_CHECK_END - TYPE_CHECK_START)) - echo "✅ TypeScript compilation successful for ${_CLIENT_LABEL} client (${TYPE_CHECK_DURATION}s)" - echo "✅ **Result**: TypeScript compilation successful" >> "$GITHUB_STEP_SUMMARY" - echo "No breaking changes detected in ${_CLIENT_LABEL} client for SDK version ${_SDK_VERSION}" >> "$GITHUB_STEP_SUMMARY" + echo "✅ TypeScript compilation successful (${TYPE_CHECK_DURATION}s)" + echo "✅ **Result**: TypeScript compilation successful" >> $GITHUB_STEP_SUMMARY + echo "No breaking changes detected for SDK version ${_SDK_VERSION}" >> $GITHUB_STEP_SUMMARY else TYPE_CHECK_END=$(date +%s) TYPE_CHECK_DURATION=$((TYPE_CHECK_END - TYPE_CHECK_START)) - echo "❌ TypeScript compilation failed for ${_CLIENT_LABEL} client after ${TYPE_CHECK_DURATION}s - breaking changes detected" - echo "❌ **Result**: TypeScript compilation failed" >> "$GITHUB_STEP_SUMMARY" - echo "Breaking changes detected in ${_CLIENT_LABEL} client for SDK version ${_SDK_VERSION}" >> "$GITHUB_STEP_SUMMARY" + echo "❌ TypeScript compilation failed after ${TYPE_CHECK_DURATION}s - breaking changes detected" + echo "❌ **Result**: TypeScript compilation failed" >> $GITHUB_STEP_SUMMARY + echo "Breaking changes detected for SDK version ${_SDK_VERSION}" >> $GITHUB_STEP_SUMMARY exit 1 - fi \ No newline at end of file + fi diff --git a/.github/workflows/test-browser-interactions.yml b/.github/workflows/test-browser-interactions.yml index fb31a93d51f..dfc0f28b9c6 100644 --- a/.github/workflows/test-browser-interactions.yml +++ b/.github/workflows/test-browser-interactions.yml @@ -18,7 +18,7 @@ jobs: id-token: write steps: - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: fetch-depth: 0 persist-credentials: false @@ -49,6 +49,8 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Generate GH App token + # NOTE: versions of actions/create-github-app-token after 2.0.3 break this workflow + # Remediation is tracked in https://bitwarden.atlassian.net/browse/PM-28174 uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3 id: app-token with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d468ca74ed6..f53bfc39d36 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -62,7 +62,7 @@ jobs: run: npm test -- --coverage --maxWorkers=3 - name: Report test results - uses: dorny/test-reporter@6e6a65b7a0bd2c9197df7d0ae36ac5cee784230c # v2.0.0 + uses: dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3 # v2.1.1 if: ${{ github.event.pull_request.head.repo.full_name == github.repository && !cancelled() }} with: name: Test Results @@ -71,10 +71,10 @@ jobs: fail-on-error: true - name: Upload results to codecov.io - uses: codecov/test-results-action@f2dba722c67b86c6caa034178c6e4d35335f6706 # v1.1.0 + uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1 - name: Upload test coverage - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: jest-coverage path: ./coverage/lcov.info @@ -103,7 +103,7 @@ jobs: sudo apt-get install -y gnome-keyring dbus-x11 - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -137,7 +137,7 @@ jobs: runs-on: macos-14 steps: - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -148,7 +148,7 @@ jobs: components: llvm-tools - name: Cache cargo registry - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 + uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 with: workspaces: "apps/desktop/desktop_native -> target" @@ -160,7 +160,7 @@ jobs: run: cargo llvm-cov --all-features --lcov --output-path lcov.info --workspace --no-cfg-coverage - name: Upload test coverage - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: rust-coverage path: ./apps/desktop/desktop_native/lcov.info @@ -173,24 +173,24 @@ jobs: - rust-coverage steps: - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false - name: Download jest coverage - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: jest-coverage path: ./ - name: Download rust coverage - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: rust-coverage path: ./apps/desktop/desktop_native - name: Upload coverage to codecov.io - uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2 + uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 with: files: | ./lcov.info diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index fee34d14e83..65f004149de 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -31,14 +31,15 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Generate GH App token - uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3 + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 id: app-token with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + permission-contents: write # for committing and pushing to the current branch - name: Check out target ref - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: main token: ${{ steps.app-token.outputs.token }} diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 2480eef505d..0b14f9d7444 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -4,6 +4,7 @@ import { componentWrapperDecorator } from "@storybook/angular"; import type { Preview } from "@storybook/angular"; import docJson from "../documentation.json"; + setCompodocJson(docJson); const wrapperDecorator = componentWrapperDecorator((story) => { diff --git a/apps/browser/package.json b/apps/browser/package.json index 82d2ad7ab7a..a6a88b53db0 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,11 +1,13 @@ { "name": "@bitwarden/browser", - "version": "2025.11.0", + "version": "2025.11.1", "scripts": { "build": "npm run build:chrome", "build:bit": "npm run build:bit:chrome", "build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", "build:bit:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack -c ../../bitwarden_license/bit-browser/webpack.config.js", + "build:dev:chrome": "npm run build:chrome && npm run update:dev:chrome", + "build:bit:dev:chrome": "npm run build:bit:chrome && npm run update:dev:chrome", "build:edge": "cross-env BROWSER=edge MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", "build:bit:edge": "cross-env BROWSER=edge MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack -c ../../bitwarden_license/bit-browser/webpack.config.js", "build:firefox": "cross-env BROWSER=firefox NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", @@ -55,9 +57,12 @@ "dist:bit:opera:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:bit:opera", "dist:safari:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:safari", "dist:bit:safari:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:bit:safari", + "package:dev:chrome": "npm run update:dev:chrome && ./scripts/compress.sh dev-chrome.zip", + "package:bit:dev:chrome": "npm run update:dev:chrome && ./scripts/compress.sh bit-dev-chrome.zip", "test": "jest", "test:watch": "jest --watch", "test:watch:all": "jest --watchAll", - "test:clearCache": "jest --clear-cache" + "test:clearCache": "jest --clear-cache", + "update:dev:chrome": "./scripts/update-manifest-dev.sh" } } diff --git a/apps/browser/scripts/update-manifest-dev.sh b/apps/browser/scripts/update-manifest-dev.sh new file mode 100755 index 00000000000..2823d4cb510 --- /dev/null +++ b/apps/browser/scripts/update-manifest-dev.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +#### +# Update the manifest key in the build directory. +#### + +set -e +set -u +set -x +set -o pipefail + +SCRIPT_ROOT="$(dirname "$0")" +BUILD_DIR="$SCRIPT_ROOT/../build" + +# Check if build directory exists +if [ -d "$BUILD_DIR" ]; then + cd "$BUILD_DIR" + + # Update manifest with dev public key + MANIFEST_PATH="./manifest.json" + + # Generated arbitrary public key from Chrome Dev Console to pin side-loaded extension IDs during development + DEV_PUBLIC_KEY='MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuIvjtsAVWZM0i5jFhSZcrmwgaf3KWcxM5F16LNDNeivC1EqJ+H5xNZ5R9UN5ueHA2xyyYAOlxY07OcY6CKTGJRJyefbUhszb66sdx26SV5gVkCois99fKBlsbSbd6und/BJYmoFUWvFCNNVH+OxLMqMQWjMMhM2ItLqTYi7dxRE5qd+7LwQpnGG2vTkm/O7nu8U3CtkfcIAGLsiTd7/iuytcMDnC0qFM5tJyY/5I+9QOhpUJ7Ybj3C18BDWDORhqxutWv+MSw//SgUn2/lPQrnrKq7FIVQL7FxxEPqkv4QwFvaixps1cBbMdJ1Ygit1z5JldoSyNxzCa5vVcJLecMQIDAQAB' + + MANIFEST_PATH_TMP="${MANIFEST_PATH}.tmp" + if jq --arg key "$DEV_PUBLIC_KEY" '.key = $key' "$MANIFEST_PATH" > "$MANIFEST_PATH_TMP"; then + mv "$MANIFEST_PATH_TMP" "$MANIFEST_PATH" + echo "Updated manifest key in $MANIFEST_PATH" + else + echo "ERROR: Failed to update manifest with jq" + rm -f "$MANIFEST_PATH_TMP" + exit 1 + fi +fi diff --git a/apps/browser/spec/mock-port.spec-util.ts b/apps/browser/spec/mock-port.spec-util.ts index b5f7825d8e9..39239ba8817 100644 --- a/apps/browser/spec/mock-port.spec-util.ts +++ b/apps/browser/spec/mock-port.spec-util.ts @@ -12,6 +12,13 @@ export function mockPorts() { (chrome.runtime.connect as jest.Mock).mockImplementation((portInfo) => { const port = mockDeep(); port.name = portInfo.name; + port.sender = { url: chrome.runtime.getURL("") }; + + // convert to internal port + delete (port as any).tab; + delete (port as any).documentId; + delete (port as any).documentLifecycle; + delete (port as any).frameId; // set message broadcast (port.postMessage as jest.Mock).mockImplementation((message) => { diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index ad36ba5854a..505a8404233 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "عند قفل النظام" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "عند إعادة تشغيل المتصفح" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "اسأل عن القياسات الحيوية عند الإطلاق" }, - "premiumRequired": { - "message": "حساب البريميوم مطلوب" - }, - "premiumRequiredDesc": { - "message": "هذه المِيزة متاحة فقط للعضوية المميزة." - }, "authenticationTimeout": { "message": "مهلة المصادقة" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "يجب عليك إضافة رابط الخادم الأساسي أو على الأقل بيئة مخصصة." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "بيئة مخصصة" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "تعيين كلمة مرور رئيسية" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 9a0239d2a34..086b66380b5 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -592,7 +592,10 @@ "message": "Bax" }, "viewAll": { - "message": "View all" + "message": "Hamısına bax" + }, + "viewLess": { + "message": "Daha azına bax" }, "viewLogin": { "message": "Girişə bax" @@ -625,7 +628,7 @@ "message": "Element sevimlilərə əlavə edildi" }, "itemRemovedFromFavorites": { - "message": "Element sevimlilərdən çıxarıldı" + "message": "Element sevimlilərdən xaric edildi" }, "notes": { "message": "Notlar" @@ -685,7 +688,7 @@ "message": "Ayarlarda bir kilid açma üsulu qurun" }, "sessionTimeoutHeader": { - "message": "Seans vaxt bitməsi" + "message": "Sessiya vaxt bitməsi" }, "vaultTimeoutHeader": { "message": "Seyf vaxtının bitməsi" @@ -796,6 +799,12 @@ "onLocked": { "message": "Sistem kilidlənəndə" }, + "onIdle": { + "message": "Sistem boşda olduqda" + }, + "onSleep": { + "message": "Sistem yuxu rejimində olduqda" + }, "onRestart": { "message": "Brauzer yenidən başladılanda" }, @@ -916,7 +925,7 @@ "message": "Hesabınızdan çıxış etmisiniz." }, "loginExpired": { - "message": "Giriş seansınızın müddəti bitdi." + "message": "Giriş sessiyanızın müddəti bitdi." }, "logIn": { "message": "Giriş et" @@ -1035,10 +1044,10 @@ "message": "Element saxlanıldı" }, "savedWebsite": { - "message": "Saved website" + "message": "Saxlanılan veb sayt" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "Saxlanılan veb sayt ( $COUNT$ )", "placeholders": { "count": { "content": "$1", @@ -1239,7 +1248,7 @@ "description": "Detailed error message shown when saving login details fails." }, "changePasswordWarning": { - "message": "Parolunuzu dəyişdirdikdən sonra yeni parolunuzla giriş etməli olacaqsınız. Digər cihazlardakı aktiv seanslar bir saat ərzində çıxış sonlandırılacaq." + "message": "Parolunuzu dəyişdirdikdən sonra yeni parolunuzla giriş etməli olacaqsınız. Digər cihazlardakı aktiv sessiyalar bir saat ərzində çıxış sonlandırılacaq." }, "accountRecoveryUpdateMasterPasswordSubtitle": { "message": "Hesabın geri qaytarılması prosesini tamamlamaq üçün ana parolunuzu dəyişdirin." @@ -1523,17 +1532,11 @@ "enableAutoBiometricsPrompt": { "message": "Açılışda biometrik soruşulsun" }, - "premiumRequired": { - "message": "Premium üzvlük lazımdır" - }, - "premiumRequiredDesc": { - "message": "Bu özəlliyi istifadə etmək üçün premium üzvlük lazımdır." - }, "authenticationTimeout": { "message": "Kimlik doğrulama vaxtı bitdi" }, "authenticationSessionTimedOut": { - "message": "Kimlik doğrulama seansının vaxtı bitdi. Lütfən giriş prosesini yenidən başladın." + "message": "Kimlik doğrulama sessiyasının vaxtı bitdi. Lütfən giriş prosesini yenidən başladın." }, "verificationCodeEmailSent": { "message": "Doğrulama poçtu $EMAIL$ ünvanına göndərildi.", @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Təməl server URL-sini və ya ən azı bir özəl mühiti əlavə etməlisiniz." }, + "selfHostedEnvMustUseHttps": { + "message": "URL-lər, HTTPS istifadə etməlidir." + }, "customEnvironment": { "message": "Özəl mühit" }, @@ -1695,28 +1701,28 @@ "message": "Avto-doldurmanı söndür" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "Avto-doldurmanı təsdiqlə" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "Bu sayt, saxlanılmış giriş məlumatlarınızla uyuşmur. Giriş məlumatlarınızı doldurmazdan əvvəl, güvənli sayt olduğuna əmin olun." }, "showInlineMenuLabel": { "message": "Avto-doldurma təkliflərini form xanalarında göstər" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Bitwarden verilərinizi fişinqdən necə qoruyur?" }, "currentWebsite": { - "message": "Current website" + "message": "Hazırkı veb sayt" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "Avto-doldur və bu veb saytı əlavə et" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "Əlavə etmədən avto-doldur" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "Avto-doldurulmasın" }, "showInlineMenuIdentitiesLabel": { "message": "Kimlikləri təklif kimi göstər" @@ -2152,7 +2158,7 @@ } }, "passwordSafe": { - "message": "Bu parol, veri pozuntularında qeydə alınmayıb. Rahatlıqla istifadə edə bilərsiniz." + "message": "Bu parol, veri pozuntularında qeydə alınmayıb. Əmniyyətlə istifadə edə bilərsiniz." }, "baseDomain": { "message": "Baza domeni", @@ -2222,7 +2228,7 @@ "message": "Təzəlikcə heç nə yaratmamısınız" }, "remove": { - "message": "Çıxart" + "message": "Xaric et" }, "default": { "message": "İlkin" @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Ana parolu ayarla" }, @@ -3061,10 +3070,10 @@ "message": "Ana parolu güncəllə" }, "updateMasterPasswordWarning": { - "message": "Ana parolunuz təzəlikcə təşkilatınızdakı bir inzibatçı tərəfindən dəyişdirildi. Seyfə erişmək üçün onu indi güncəlləməlisiniz. Davam etsəniz, hazırkı seansdan çıxış edəcəksiniz və təkrar giriş etməli olacaqsınız. Digər cihazlardakı aktiv seanslar bir saata qədər aktiv qalmağa davam edə bilər." + "message": "Ana parolunuz təzəlikcə təşkilatınızdakı bir inzibatçı tərəfindən dəyişdirildi. Seyfə erişmək üçün onu indi güncəlləməlisiniz. Davam etsəniz, hazırkı sessiyadan çıxış edəcəksiniz və təkrar giriş etməli olacaqsınız. Digər cihazlardakı aktiv sessiyalar bir saata qədər aktiv qalmağa davam edə bilər." }, "updateWeakMasterPasswordWarning": { - "message": "Ana parolunuz təşkilatınızdakı siyasətlərdən birinə və ya bir neçəsinə uyğun gəlmir. Seyfə erişmək üçün ana parolunuzu indi güncəlləməlisiniz. Davam etsəniz, hazırkı seansdan çıxış etmiş və təkrar giriş etməli olacaqsınız. Digər cihazlardakı aktiv seanslar bir saata qədər aktiv qalmağa davam edə bilər." + "message": "Ana parolunuz təşkilatınızdakı siyasətlərdən birinə və ya bir neçəsinə uyğun gəlmir. Seyfə erişmək üçün ana parolunuzu indi güncəlləməlisiniz. Davam etsəniz, hazırkı sessiyadan çıxış etmiş və təkrar giriş etməli olacaqsınız. Digər cihazlardakı aktiv sessiyalar bir saata qədər aktiv qalmağa davam edə bilər." }, "tdeDisabledMasterPasswordRequired": { "message": "Təşkilatınız, güvənli cihaz şifrələməsini sıradan çıxartdı. Seyfinizə erişmək üçün lütfən ana parol təyin edin." @@ -3220,7 +3229,7 @@ "message": "Simvol sayını dəyişdir" }, "sessionTimeout": { - "message": "Seansınızın vaxtı bitdi. Lütfən geri qayıdıb yenidən giriş etməyə cəhd edin." + "message": "Sessiyanızın vaxtı bitdi. Lütfən geri qayıdıb yenidən giriş etməyə cəhd edin." }, "exportingPersonalVaultTitle": { "message": "Fərdi seyfin xaricə köçürülməsi" @@ -3280,7 +3289,7 @@ "message": "Şifrə açma xətası" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "Avto-doldurma verilərini alma xətası" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden, aşağıda sadalanan seyf element(lər)inin şifrəsini aça bilmədi." @@ -3726,7 +3735,7 @@ "message": "Cihazları idarə et" }, "currentSession": { - "message": "Hazırkı seans" + "message": "Hazırkı sessiya" }, "mobile": { "message": "Mobil", @@ -3923,7 +3932,7 @@ "message": "İstifadəçiyə güvən" }, "sendsTitleNoItems": { - "message": "Send, həssas məlumatlar təhlükəsizdir", + "message": "Send ilə həssas məlumatlar əmniyyətdədir", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsBodyNoItems": { @@ -4054,13 +4063,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "Avto-doldurula bilmir" }, "cannotAutofillExactMatch": { "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." }, "okay": { - "message": "Okay" + "message": "Oldu" }, "toggleSideNavigation": { "message": "Yan naviqasiyanı aç/bağla" @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "İlkin ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "$WEBSITE$ ilə uyuşma aşkarlamasını göstər", "placeholders": { @@ -5702,7 +5721,7 @@ "message": "Kimliklərinizlə, uzun qeydiyyat və ya əlaqə xanalarını daha tez avtomatik doldurun." }, "newNoteNudgeTitle": { - "message": "Həssas verilərinizi güvənli şəkildə saxlayın" + "message": "Həssas verilərinizi əmniyyətdə saxlayın" }, "newNoteNudgeBody": { "message": "Notlarla, bankçılıq və ya sığorta təfsilatları kimi həssas veriləri təhlükəsiz saxlayın." @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Riskli girişlərinizi güvənli hala gətirməyiniz əladır!" }, + "upgradeNow": { + "message": "İndi yüksəlt" + }, + "builtInAuthenticator": { + "message": "Daxili kimlik doğrulayıcı" + }, + "secureFileStorage": { + "message": "Güvənli fayl anbarı" + }, + "emergencyAccess": { + "message": "Fövqəladə hal erişimi" + }, + "breachMonitoring": { + "message": "Pozuntu monitorinqi" + }, + "andMoreFeatures": { + "message": "Və daha çoxu!" + }, + "planDescPremium": { + "message": "Tam onlayn təhlükəsizlik" + }, + "upgradeToPremium": { + "message": "\"Premium\"a yüksəlt" + }, + "upgradeCompleteSecurity": { + "message": "Tam təhlükəsizlik üçün yüksəldin" + }, + "premiumGivesMoreTools": { + "message": "Premium, güvəndə qalmağınız, səmərəli çalışmağınız və nəzarətə sahib olmağınız üçün daha çox alət verir." + }, + "explorePremium": { + "message": "Premium-u kəşf et" + }, + "loadingVault": { + "message": "Seyf yüklənir" + }, + "vaultLoaded": { + "message": "Seyf yükləndi" + }, "settingDisabledByPolicy": { "message": "Bu ayar, təşkilatınızın siyasəti tərəfindən sıradan çıxarılıb.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Kart nömrəsi" + }, + "sessionTimeoutSettingsAction": { + "message": "Vaxt bitmə əməliyyatı" } } diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 35aaddc13b2..16a6d739962 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Пры блакіраванні сістэмы" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Пры перазапуску браўзера" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Пытацца пра біяметрыю пры запуску" }, - "premiumRequired": { - "message": "Патрабуецца прэміяльны статус" - }, - "premiumRequiredDesc": { - "message": "Для выкарыстання гэтай функцыі патрабуецца прэміяльны статус." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Карыстальніцкае асяроддзе" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Прызначыць асноўны пароль" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 68b962837eb..3dba65d6aa7 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Показване на всички" }, + "viewLess": { + "message": "Преглед на по-малко" + }, "viewLogin": { "message": "Преглед на елемента за вписване" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "При заключване на системата" }, + "onIdle": { + "message": "При бездействие на системата" + }, + "onSleep": { + "message": "При заспиване на системата" + }, "onRestart": { "message": "При повторно пускане на браузъра" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Питане за биометрични данни при пускане" }, - "premiumRequired": { - "message": "Изисква се платен абонамент" - }, - "premiumRequiredDesc": { - "message": "За да се възползвате от тази възможност, трябва да ползвате платен абонамент." - }, "authenticationTimeout": { "message": "Време на давност за удостоверяването" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Трябва да добавите или основния адрес на сървъра, или поне една специална среда." }, + "selfHostedEnvMustUseHttps": { + "message": "Адресите трябва да ползват HTTPS." + }, "customEnvironment": { "message": "Специална среда" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Задаване на главна парола" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "По подразбиране ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Показване на откритото съвпадение $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Добра работа с подсигуряването на данните за вписване в риск!" }, + "upgradeNow": { + "message": "Надграждане сега" + }, + "builtInAuthenticator": { + "message": "Вграден удостоверител" + }, + "secureFileStorage": { + "message": "Сигурно съхранение на файлове" + }, + "emergencyAccess": { + "message": "Авариен достъп" + }, + "breachMonitoring": { + "message": "Наблюдение за пробиви" + }, + "andMoreFeatures": { + "message": "И още!" + }, + "planDescPremium": { + "message": "Пълна сигурност в Интернет" + }, + "upgradeToPremium": { + "message": "Надградете до Платения план" + }, + "upgradeCompleteSecurity": { + "message": "Надградете, за да се възползвате от пълна защита" + }, + "premiumGivesMoreTools": { + "message": "Платеният план предоставя повече инструменти за защита, ефективна работа и контрол." + }, + "explorePremium": { + "message": "Разгледайте платения план" + }, + "loadingVault": { + "message": "Зареждане на трезора" + }, + "vaultLoaded": { + "message": "Трезорът е зареден" + }, "settingDisabledByPolicy": { "message": "Тази настройка е изключена съгласно политиката на организацията Ви.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Номер на картата" + }, + "sessionTimeoutSettingsAction": { + "message": "Действие при изтичането на времето за достъп" } } diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 25e37c06745..d2519cb13e3 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "সিস্টেম লকে" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "ব্রাউজার পুনঃসূচনাই" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "প্রিমিয়াম আবশ্যক" - }, - "premiumRequiredDesc": { - "message": "এই বৈশিষ্ট্যটি ব্যবহার করতে একটি প্রিমিয়াম সদস্যতার প্রয়োজন।" - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "পছন্দসই পরিবেশ" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "মূল পাসওয়ার্ড ধার্য করুন" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 566f0e7077e..917180579f2 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Premium required" - }, - "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index a5e00afae0c..a9ebdf139d7 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "En bloquejar el sistema" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "En reiniciar el navegador" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Demaneu dades biometriques en iniciar" }, - "premiumRequired": { - "message": "Premium requerit" - }, - "premiumRequiredDesc": { - "message": "Cal una subscripció premium per utilitzar aquesta característica." - }, "authenticationTimeout": { "message": "Temps d'espera d'autenticació" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Entorn personalitzat" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Estableix la contrasenya mestra" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 5dd4a6a6efc..ca5d4b09f28 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Zobrazit vše" }, + "viewLess": { + "message": "Zobrazit méně" + }, "viewLogin": { "message": "Zobrazit přihlašovací údaje" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Při uzamknutí systému" }, + "onIdle": { + "message": "Při nečinnosti systému" + }, + "onSleep": { + "message": "Při přechodu do režimu spánku" + }, "onRestart": { "message": "Při restartu prohlížeče" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ověřit biometrické údaje při spuštění" }, - "premiumRequired": { - "message": "Je vyžadováno členství Premium" - }, - "premiumRequiredDesc": { - "message": "Pro použití této funkce je potřebné členství Premium." - }, "authenticationTimeout": { "message": "Časový limit ověření" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Musíte přidat buď základní adresu URL serveru nebo alespoň jedno vlastní prostředí." }, + "selfHostedEnvMustUseHttps": { + "message": "URL adresy musí používat HTTPS." + }, "customEnvironment": { "message": "Vlastní prostředí" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "Tato stránka narušuje zážitek z Bitwardenu. Vložené menu Bitwarden bylo dočasně vypnuto jako bezpečnostní opatření." + }, "setMasterPassword": { "message": "Nastavit hlavní heslo" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Výchozí ($VALUE$)", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Zobrazit detekci shody $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Skvělá práce při zabezpečení přihlašovacích údajů v ohrožení!" }, + "upgradeNow": { + "message": "Aktualizovat nyní" + }, + "builtInAuthenticator": { + "message": "Vestavěný autentifikátor" + }, + "secureFileStorage": { + "message": "Zabezpečené úložiště souborů" + }, + "emergencyAccess": { + "message": "Nouzový přístup" + }, + "breachMonitoring": { + "message": "Sledování úniků" + }, + "andMoreFeatures": { + "message": "A ještě více!" + }, + "planDescPremium": { + "message": "Dokončit online zabezpečení" + }, + "upgradeToPremium": { + "message": "Aktualizovat na Premium" + }, + "upgradeCompleteSecurity": { + "message": "Aktualizujte pro úplné zabezpečení" + }, + "premiumGivesMoreTools": { + "message": "Verze Premium Vám poskytne více nástrojů k zabezpečení, efektivní práci a udržení kontroly." + }, + "explorePremium": { + "message": "Objevit Premium" + }, + "loadingVault": { + "message": "Načítání trezoru" + }, + "vaultLoaded": { + "message": "Trezor byl načten" + }, "settingDisabledByPolicy": { "message": "Toto nastavení je zakázáno zásadami Vaší organizace.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Číslo karty" + }, + "sessionTimeoutSettingsAction": { + "message": "Akce vypršení časového limitu" } } diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 1f46f034f5e..1f83ff72f62 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "wrth ailgychwyn y porwr" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Mae angen aelodaeth uwch" - }, - "premiumRequiredDesc": { - "message": "Mae angen aelodaeth uwch i ddefnyddio'r nodwedd hon." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Amgylchedd addasedig" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Gosod prif gyfrinair" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 7e1f66478cf..96aa81ce876 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Når systemet låses" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Ved genstart af browseren" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Bed om biometri ved start" }, - "premiumRequired": { - "message": "Premium påkrævet" - }, - "premiumRequiredDesc": { - "message": "Premium-medlemskab kræves for at anvende denne funktion." - }, "authenticationTimeout": { "message": "Godkendelsestimeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Der skal tilføjes enten basis server-URL'en eller mindst ét tilpasset miljø." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Brugerdefineret miljø" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Indstil hovedadgangskode" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Vis matchdetektion $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 9527c15e6a3..b32c9a68c06 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -29,10 +29,10 @@ "message": "Mit Passkey anmelden" }, "useSingleSignOn": { - "message": "Single Sign-on verwenden" + "message": "Single Sign-On verwenden" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "Deine Organisation erfordert Single Sign-On." }, "welcomeBack": { "message": "Willkommen zurück" @@ -44,7 +44,7 @@ "message": "Schließe die Erstellung deines Kontos ab, indem du ein Passwort festlegst" }, "enterpriseSingleSignOn": { - "message": "Enterprise Single-Sign-On" + "message": "Enterprise Single Sign-On" }, "cancel": { "message": "Abbrechen" @@ -592,7 +592,10 @@ "message": "Anzeigen" }, "viewAll": { - "message": "View all" + "message": "Alles anzeigen" + }, + "viewLess": { + "message": "Weniger anzeigen" }, "viewLogin": { "message": "Zugangsdaten anzeigen" @@ -796,6 +799,12 @@ "onLocked": { "message": "Wenn System gesperrt" }, + "onIdle": { + "message": "Bei Systeminaktivität" + }, + "onSleep": { + "message": "Im Standby" + }, "onRestart": { "message": "Bei Browser-Neustart" }, @@ -1035,10 +1044,10 @@ "message": "Eintrag gespeichert" }, "savedWebsite": { - "message": "Saved website" + "message": "Website gespeichert" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "Gespeicherte Websites ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Beim Start nach biometrischen Daten fragen" }, - "premiumRequired": { - "message": "Premium-Mitgliedschaft benötigt" - }, - "premiumRequiredDesc": { - "message": "Eine Premium-Mitgliedschaft ist für diese Funktion notwendig." - }, "authenticationTimeout": { "message": "Authentifizierungs-Timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Du musst entweder die Basis-Server-URL oder mindestens eine benutzerdefinierte Umgebung hinzufügen." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs müssen HTTPS verwenden." + }, "customEnvironment": { "message": "Benutzerdefinierte Umgebung" }, @@ -1695,28 +1701,28 @@ "message": "Auto-Ausfüllen deaktivieren" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "Auto-Ausfüllen bestätigen" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "Diese Website stimmt nicht mit deinen gespeicherten Zugangsdaten überein. Bevor du deine Zugangsdaten eingibst, stelle sicher, dass es sich um eine vertrauenswürdige Website handelt." }, "showInlineMenuLabel": { "message": "Vorschläge zum Auto-Ausfüllen in Formularfeldern anzeigen" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Wie schützt Bitwarden deine Daten vor Phishing?" }, "currentWebsite": { - "message": "Current website" + "message": "Aktuelle Website" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "Auto-Ausfüllen und diese Website hinzufügen" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "Auto-Ausfüllen ohne Hinzufügen" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "Nicht automatisch ausfüllen" }, "showInlineMenuIdentitiesLabel": { "message": "Identitäten als Vorschläge anzeigen" @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Master-Passwort festlegen" }, @@ -3280,7 +3289,7 @@ "message": "Entschlüsselungsfehler" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "Fehler beim Abrufen der Auto-Ausfüllen-Daten" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden konnte folgende(n) Tresor-Eintrag/Einträge nicht entschlüsseln." @@ -4054,10 +4063,10 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "Kein Auto-Ausfüllen möglich" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "Die Standard-Übereinstimmungserkennung steht auf \"Exakte Übereinstimmung\". Die aktuelle Website stimmt nicht genau mit den gespeicherten Zugangsdaten für diesen Eintrag überein." }, "okay": { "message": "Okay" @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Standard ($VALUE$)", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Übereinstimmungs-Erkennung anzeigen $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Gute Arbeit! Du hast deine gefährdeten Zugangsdaten geschützt!" }, + "upgradeNow": { + "message": "Jetzt upgraden" + }, + "builtInAuthenticator": { + "message": "Integrierter Authenticator" + }, + "secureFileStorage": { + "message": "Sicherer Dateispeicher" + }, + "emergencyAccess": { + "message": "Notfallzugriff" + }, + "breachMonitoring": { + "message": "Datendiebstahl-Überwachung" + }, + "andMoreFeatures": { + "message": "Und mehr!" + }, + "planDescPremium": { + "message": "Umfassende Online-Sicherheit" + }, + "upgradeToPremium": { + "message": "Upgrade auf Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade für umfassende Sicherheit" + }, + "premiumGivesMoreTools": { + "message": "Premium gibt dir mehr Werkzeuge, um sicher zu bleiben, effizient zu arbeiten und die Kontrolle zu behalten." + }, + "explorePremium": { + "message": "Premium erkunden" + }, + "loadingVault": { + "message": "Tresor wird geladen" + }, + "vaultLoaded": { + "message": "Tresor geladen" + }, "settingDisabledByPolicy": { "message": "Diese Einstellung ist durch die Richtlinien deiner Organisation deaktiviert.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Kartennummer" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout-Aktion" } } diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 230f5d60423..f4c3c0d53a5 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Προβολή σύνδεσης" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Κατά το Κλείδωμα Συστήματος" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Κατά την Επανεκκίνηση του Browser" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ζητήστε βιομετρικά κατά την εκκίνηση" }, - "premiumRequired": { - "message": "Απαιτείται το Premium" - }, - "premiumRequiredDesc": { - "message": "Για να χρησιμοποιήσετε αυτή τη λειτουργία, απαιτείται συνδρομή Premium." - }, "authenticationTimeout": { "message": "Χρονικό όριο επαλήθευσης" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Πρέπει να προσθέσετε είτε το βασικό URL του διακομιστή ή τουλάχιστον ένα προσαρμοσμένο περιβάλλον." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Προσαρμοσμένο περιβάλλον" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Ορισμός κύριου κωδικού πρόσβασης" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Εμφάνιση ανιχνεύσεων αντιστοίχισης $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 888123af7cb..748a771cc5c 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -585,6 +585,9 @@ "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" }, + "upgradeToUseArchive": { + "message": "A premium membership is required to use Archive." + }, "edit": { "message": "Edit" }, @@ -594,6 +597,12 @@ "viewAll": { "message": "View all" }, + "showAll": { + "message": "Show all" + }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +805,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -2448,6 +2463,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, @@ -4911,6 +4929,9 @@ "premium": { "message": "Premium" }, + "unlockFeaturesWithPremium": { + "message": "Unlock reporting, emergency access, and more security features with Premium." + }, "freeOrgsCannotUseAttachments": { "message": "Free organizations cannot use attachments" }, @@ -4995,6 +5016,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5811,6 +5842,21 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "unlockAdvancedSecurity": { + "message": "Unlock advanced security features" + }, + "unlockAdvancedSecurityDesc": { + "message": "A Premium subscription gives you more tools to stay secure and in control" + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5820,5 +5866,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 2058d68c55b..96c3323faef 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Premium required" - }, - "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organisation's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 6c1b1e01139..b9f777148e3 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Premium required" - }, - "premiumRequiredDesc": { - "message": "A premium membership is required to use this feature." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organisation's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 1284563a6e3..92dbe15fad2 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Al bloquear el sistema" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Al reiniciar el navegador" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Pedir datos biométricos al ejecutar" }, - "premiumRequired": { - "message": "Premium requerido" - }, - "premiumRequiredDesc": { - "message": "Una membrasía Premium es requerida para utilizar esta característica." - }, "authenticationTimeout": { "message": "Tiempo de autenticación agotado" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Debes añadir la dirección URL del servidor base o al menos un entorno personalizado." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Entorno personalizado" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Establecer contraseña maestra" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 3f163506214..bb029bf7777 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Arvutist väljalogimisel" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Brauseri taaskäivitamisel" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Küsi avamisel biomeetriat" }, - "premiumRequired": { - "message": "Vajalik on Premium versioon" - }, - "premiumRequiredDesc": { - "message": "Selle funktsiooni kasutamiseks on vajalik tasulist kontot omada." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Kohandatud keskkond" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Määra ülemparool" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index f74233193ef..06a4f8ea48d 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Sistema blokeatzean" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Nabigatzailea berrabiaraztean" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Biometria eskatu saioa hastean" }, - "premiumRequired": { - "message": "Premium izatea beharrezkoa da" - }, - "premiumRequiredDesc": { - "message": "Premium bazkidetza beharrezkoa da ezaugarri hau erabiltzeko." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Ingurune pertsonalizatua" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Ezarri pasahitz nagusia" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 6b52f1d4364..33f4a02277d 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "هنگام قفل سیستم" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "هنگام راه‌اندازی مجدد" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "درخواست بیومتریک هنگام راه‌اندازی" }, - "premiumRequired": { - "message": "در نسخه پرمیوم کار می‌کند" - }, - "premiumRequiredDesc": { - "message": "برای استفاده از این ویژگی عضویت پرمیوم لازم است." - }, "authenticationTimeout": { "message": "پایان زمان احراز هویت" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "شما باید یا نشانی اینترنتی پایه سرور را اضافه کنید، یا حداقل یک محیط سفارشی تعریف کنید." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "محیط سفارشی" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "تنظیم کلمه عبور اصلی" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "نمایش شناسایی تطابق برای $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index a0e6fce06bd..9953782f504 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -554,21 +554,21 @@ "message": "Nollaa haku" }, "archiveNoun": { - "message": "Archive", + "message": "Arkistoi", "description": "Noun" }, "archiveVerb": { - "message": "Archive", + "message": "Arkistoi", "description": "Verb" }, "unArchive": { - "message": "Unarchive" + "message": "Poista arkistosta" }, "itemsInArchive": { - "message": "Items in archive" + "message": "Arkistossa olevat kohteet" }, "noItemsInArchive": { - "message": "No items in archive" + "message": "Arkistossa ei ole kohteita" }, "noItemsInArchiveDesc": { "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." @@ -580,7 +580,7 @@ "message": "Item was unarchived" }, "archiveItem": { - "message": "Archive item" + "message": "Arkistoi kohde" }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" @@ -592,7 +592,10 @@ "message": "Näytä" }, "viewAll": { - "message": "View all" + "message": "Näytä kaikki" + }, + "viewLess": { + "message": "View less" }, "viewLogin": { "message": "View login" @@ -796,6 +799,12 @@ "onLocked": { "message": "Kun järjestelmä lukitaan" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Kun selain käynnistetään uudelleen" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Pyydä Biometristä todennusta käynnistettäessä" }, - "premiumRequired": { - "message": "Premium vaaditaan" - }, - "premiumRequiredDesc": { - "message": "Tämä ominaisuus edellyttää Premium-jäsenyyttä." - }, "authenticationTimeout": { "message": "Todennuksen aikakatkaisu" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Sinun on lisättävä joko palvelimen perusosoite tai ainakin yksi mukautettu palvelinympäristö." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Mukautettu palvelinympäristö" }, @@ -2009,11 +2015,11 @@ "message": "Muistiinpano" }, "newItemHeaderLogin": { - "message": "New Login", + "message": "Uusi kirjautumistieto", "description": "Header for new login item type" }, "newItemHeaderCard": { - "message": "New Card", + "message": "Uusi kortti", "description": "Header for new card item type" }, "newItemHeaderIdentity": { @@ -2025,23 +2031,23 @@ "description": "Header for new note item type" }, "newItemHeaderSshKey": { - "message": "New SSH key", + "message": "Uusi SSH-avain", "description": "Header for new SSH key item type" }, "newItemHeaderTextSend": { - "message": "New Text Send", + "message": "Uusi teksti-Send", "description": "Header for new text send" }, "newItemHeaderFileSend": { - "message": "New File Send", + "message": "Uusi tiedosto-Send", "description": "Header for new file send" }, "editItemHeaderLogin": { - "message": "Edit Login", + "message": "Muokkaa kirjautumistietoa", "description": "Header for edit login item type" }, "editItemHeaderCard": { - "message": "Edit Card", + "message": "Muokkaa korttia", "description": "Header for edit card item type" }, "editItemHeaderIdentity": { @@ -2053,23 +2059,23 @@ "description": "Header for edit note item type" }, "editItemHeaderSshKey": { - "message": "Edit SSH key", + "message": "Muokkaa SSH avainta", "description": "Header for edit SSH key item type" }, "editItemHeaderTextSend": { - "message": "Edit Text Send", + "message": "Muokkaa teksti-Sendiä", "description": "Header for edit text send" }, "editItemHeaderFileSend": { - "message": "Edit File Send", + "message": "Muokkaa tiedosto-Sendiä", "description": "Header for edit file send" }, "viewItemHeaderLogin": { - "message": "View Login", + "message": "Näytä kirjautumistieto", "description": "Header for view login item type" }, "viewItemHeaderCard": { - "message": "View Card", + "message": "Näytä kortti", "description": "Header for view card item type" }, "viewItemHeaderIdentity": { @@ -2081,7 +2087,7 @@ "description": "Header for view note item type" }, "viewItemHeaderSshKey": { - "message": "View SSH key", + "message": "Näytä SSH-avain", "description": "Header for view SSH key item type" }, "passwordHistory": { @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Aseta pääsalasana" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Oletus ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Näytä vastaavuuden tunnistus $WEBSITE$", "placeholders": { @@ -5640,7 +5659,7 @@ "message": "Close this tab" }, "phishingPageContinueV2": { - "message": "Continue to this site (not recommended)" + "message": "Jatka tälle sivustolle (ei suositeltavaa)" }, "phishingPageExplanation1": { "message": "This site was found in ", @@ -5757,7 +5776,7 @@ "message": "Show less" }, "next": { - "message": "Next" + "message": "Seuraava" }, "moreBreadcrumbs": { "message": "More breadcrumbs", @@ -5769,14 +5788,56 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Ladataan holvia" + }, + "vaultLoaded": { + "message": "Holvi ladattu" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." }, "zipPostalCodeLabel": { - "message": "ZIP / Postal code" + "message": "Postinumero" }, "cardNumberLabel": { - "message": "Card number" + "message": "Kortin numero" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 3c249b0a350..687863550a7 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Sa pag-lock ng sistema" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Sa pag-restart ng browser" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Mangyaring humingi ng mga biometrika sa paglunsad" }, - "premiumRequired": { - "message": "Premium na kinakailangan" - }, - "premiumRequiredDesc": { - "message": "Ang Premium na membership ay kinakailangan upang gamitin ang tampok na ito." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Kapaligirang Custom" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Itakda ang master password" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index e3a153fe34f..87c1a20a38a 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -32,7 +32,7 @@ "message": "Utiliser l'authentification unique" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "Votre organisation exige l’authentification unique." }, "welcomeBack": { "message": "Content de vous revoir" @@ -592,7 +592,10 @@ "message": "Afficher" }, "viewAll": { - "message": "View all" + "message": "Tout afficher" + }, + "viewLess": { + "message": "Afficher moins" }, "viewLogin": { "message": "Afficher l'Identifiant" @@ -796,6 +799,12 @@ "onLocked": { "message": "Au verrouillage" }, + "onIdle": { + "message": "À l'inactivité du système" + }, + "onSleep": { + "message": "À la mise en veille du système" + }, "onRestart": { "message": "Au redémarrage du navigateur" }, @@ -1035,10 +1044,10 @@ "message": "Élément enregistré" }, "savedWebsite": { - "message": "Saved website" + "message": "Site Web enregistré" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "Sites Web enregistrés ( $COUNT$)", "placeholders": { "count": { "content": "$1", @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Demander la biométrie au lancement" }, - "premiumRequired": { - "message": "Premium requis" - }, - "premiumRequiredDesc": { - "message": "Une adhésion Premium est requise pour utiliser cette fonctionnalité." - }, "authenticationTimeout": { "message": "Délai d'authentification dépassé" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Vous devez ajouter soit l'URL du serveur de base, soit au moins un environnement personnalisé." }, + "selfHostedEnvMustUseHttps": { + "message": "Les URL doivent utiliser HTTPS." + }, "customEnvironment": { "message": "Environnement personnalisé" }, @@ -1695,28 +1701,28 @@ "message": "Désactiver la saisie automatique" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "Confirmer la saisie automatique" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "Ce site ne correspond pas à vos identifiants de connexion enregistrés. Avant de remplir vos identifiants de connexion, assurez-vous que c'est un site de confiance." }, "showInlineMenuLabel": { "message": "Afficher les suggestions de saisie automatique dans les champs d'un formulaire" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Comment Bitwarden protège-t-il vos données contre l'hameçonnage ?" }, "currentWebsite": { - "message": "Current website" + "message": "Site internet actuel" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "Saisir automatiquement et ajouter ce site" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "Saisir automatiquement sans ajouter" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "Ne pas saisir automatiquement" }, "showInlineMenuIdentitiesLabel": { "message": "Afficher les identités sous forme de suggestions" @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Définir le mot de passe principal" }, @@ -3280,13 +3289,13 @@ "message": "Erreur de déchiffrement" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "Erreur lors de l'obtention des données de saisie automatique" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden n’a pas pu déchiffrer le(s) élément(s) du coffre listé(s) ci-dessous." }, "contactCSToAvoidDataLossPart1": { - "message": "Contacter le service clientèle", + "message": "Contacter succès client", "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "contactCSToAvoidDataLossPart2": { @@ -4054,13 +4063,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "Impossible de saisir automatiquement" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "La correspondance par défaut est définie à 'Correspondance exacte'. Le site internet actuel ne correspond pas exactement aux informations de l'identifiant de connexion enregistrées pour cet élément." }, "okay": { - "message": "Okay" + "message": "Ok" }, "toggleSideNavigation": { "message": "Basculer la navigation latérale" @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Par défaut ($VALUE$)", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Afficher la détection de correspondance $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Excellent travail pour sécuriser vos identifiants à risque !" }, + "upgradeNow": { + "message": "Mettre à niveau maintenant" + }, + "builtInAuthenticator": { + "message": "Authentificateur intégré" + }, + "secureFileStorage": { + "message": "Stockage sécurisé de fichier" + }, + "emergencyAccess": { + "message": "Accès d'urgence" + }, + "breachMonitoring": { + "message": "Surveillance des fuites" + }, + "andMoreFeatures": { + "message": "Et encore plus !" + }, + "planDescPremium": { + "message": "Sécurité en ligne complète" + }, + "upgradeToPremium": { + "message": "Mettre à niveau vers Premium" + }, + "upgradeCompleteSecurity": { + "message": "Mettre à niveau pour une sécurité complète" + }, + "premiumGivesMoreTools": { + "message": "Premium vous donne plus d'outils pour rester en sécurité, travailler efficacement et garder le contrôle." + }, + "explorePremium": { + "message": "Explorer Premium" + }, + "loadingVault": { + "message": "Chargement du coffre" + }, + "vaultLoaded": { + "message": "Coffre chargé" + }, "settingDisabledByPolicy": { "message": "Ce paramètre est désactivé par la politique de sécurité de votre organisation.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Numéro de carte" + }, + "sessionTimeoutSettingsAction": { + "message": "Action à l’expiration" } } diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 6a13ce033b1..9b35af1aad4 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Ó bloquear o sistema" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Ó reiniciar o navegador" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Requirir biometría no inicio" }, - "premiumRequired": { - "message": "Plan Prémium requirido" - }, - "premiumRequiredDesc": { - "message": "Requírese un plan Prémium para poder empregar esta función." - }, "authenticationTimeout": { "message": "Tempo límite de autenticación superado" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Debes engadir ou a URL base do servidor ou, polo menos, un entorno personalizado." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Entorno personalizado" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Definir contrasinal mestre" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Mostrar detección de coincidencia $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 3834745f8e9..7d1700cbbdc 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -32,7 +32,7 @@ "message": "השתמש בכניסה יחידה" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "הארגון שלך דורש כניסה יחידה." }, "welcomeBack": { "message": "ברוך שובך" @@ -554,36 +554,36 @@ "message": "אפס חיפוש" }, "archiveNoun": { - "message": "Archive", + "message": "ארכיון", "description": "Noun" }, "archiveVerb": { - "message": "Archive", + "message": "העבר לארכיון", "description": "Verb" }, "unArchive": { - "message": "Unarchive" + "message": "הסר מהארכיון" }, "itemsInArchive": { - "message": "Items in archive" + "message": "פריטים בארכיון" }, "noItemsInArchive": { - "message": "No items in archive" + "message": "אין פריטים בארכיון" }, "noItemsInArchiveDesc": { - "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + "message": "פריטים בארכיון יופיעו כאן ויוחרגו מתוצאות חיפוש כללי והצעות למילוי אוטומטי." }, "itemWasSentToArchive": { - "message": "Item was sent to archive" + "message": "הפריט נשלח לארכיון" }, "itemUnarchived": { - "message": "Item was unarchived" + "message": "הפריט הוסר מהארכיון" }, "archiveItem": { - "message": "Archive item" + "message": "העבר פריט לארכיון" }, "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "message": "פריטים בארכיון מוחרגים מתוצאות חיפוש כללי והצעות למילוי אוטומטי. האם אתה בטוח שברצונך להעביר פריט זה לארכיון?" }, "edit": { "message": "ערוך" @@ -592,7 +592,10 @@ "message": "הצג" }, "viewAll": { - "message": "View all" + "message": "הצג הכל" + }, + "viewLess": { + "message": "הצג פחות" }, "viewLogin": { "message": "הצג כניסה" @@ -740,7 +743,7 @@ "message": "סיסמה ראשית שגויה" }, "invalidMasterPasswordConfirmEmailAndHost": { - "message": "Invalid master password. Confirm your email is correct and your account was created on $HOST$.", + "message": "סיסמה ראשית אינה תקינה. יש לאשר שהדוא\"ל שלך נכון ושהחשבון שלך נוצר ב־$HOST$.", "placeholders": { "host": { "content": "$1", @@ -796,6 +799,12 @@ "onLocked": { "message": "בנעילת המערכת" }, + "onIdle": { + "message": "כשהמערכת מזהה חוסר פעילות" + }, + "onSleep": { + "message": "כשהמערכת נכנסת למצב שינה" + }, "onRestart": { "message": "בהפעלת הדפדפן מחדש" }, @@ -1035,10 +1044,10 @@ "message": "הפריט נשמר" }, "savedWebsite": { - "message": "Saved website" + "message": "אתר אינטרנט שנשמר" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "אתרי אינטרנט שנשמרו ( $COUNT$ )", "placeholders": { "count": { "content": "$1", @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "בקש זיהוי ביומטרי בפתיחה" }, - "premiumRequired": { - "message": "נדרש פרימיום" - }, - "premiumRequiredDesc": { - "message": "נדרשת חברות פרימיום כדי להשתמש בתכונה זו." - }, "authenticationTimeout": { "message": "פסק זמן לאימות" }, @@ -1567,13 +1570,13 @@ "message": "קרא מפתח אבטחה" }, "readingPasskeyLoading": { - "message": "Reading passkey..." + "message": "קורא מפתח גישה..." }, "passkeyAuthenticationFailed": { - "message": "Passkey authentication failed" + "message": "אימות מפתח גישה נכשל" }, "useADifferentLogInMethod": { - "message": "Use a different log in method" + "message": "השתמש בשיטת כניסה אחרת" }, "awaitingSecurityKeyInteraction": { "message": "ממתין לאינטראקציה עם מפתח אבטחה..." @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "אתה מוכרח להוסיף או את בסיס ה־URL של השרת או לפחות סביבה מותאמת אישית אחת." }, + "selfHostedEnvMustUseHttps": { + "message": "כתובות URL מוכרחות להשתמש ב־HTTPS." + }, "customEnvironment": { "message": "סביבה מותאמת אישית" }, @@ -1695,28 +1701,28 @@ "message": "השבת מילוי אוטומטי" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "אשר מילוי אוטומטי" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "אתר זה אינו תואם את פרטי הכניסה השמורה שלך. לפני שאתה ממלא את אישורי הכניסה שלך, וודא שזהו אתר מהימן." }, "showInlineMenuLabel": { "message": "הצג הצעות למילוי אוטומטי על שדות טופס" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "כיצד Bitwarden מגנה על הנתונים שלך מדיוג?" }, "currentWebsite": { - "message": "Current website" + "message": "אתר נוכחי" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "מלא אוטומטית והוסף אתר אינטרנט זה" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "מלא אוטומטית מבלי להוסיף" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "אל תמלא אוטומטית" }, "showInlineMenuIdentitiesLabel": { "message": "הצג זהויות כהצעות" @@ -2009,79 +2015,79 @@ "message": "הערה" }, "newItemHeaderLogin": { - "message": "New Login", + "message": "כניסה חדשה", "description": "Header for new login item type" }, "newItemHeaderCard": { - "message": "New Card", + "message": "כרטיס חדש", "description": "Header for new card item type" }, "newItemHeaderIdentity": { - "message": "New Identity", + "message": "זהות חדשה", "description": "Header for new identity item type" }, "newItemHeaderNote": { - "message": "New Note", + "message": "הערה חדשה", "description": "Header for new note item type" }, "newItemHeaderSshKey": { - "message": "New SSH key", + "message": "מפתח SSH חדש", "description": "Header for new SSH key item type" }, "newItemHeaderTextSend": { - "message": "New Text Send", + "message": "סֵנְד של טקסט חדש", "description": "Header for new text send" }, "newItemHeaderFileSend": { - "message": "New File Send", + "message": "סֵנְד של קובץ חדש", "description": "Header for new file send" }, "editItemHeaderLogin": { - "message": "Edit Login", + "message": "ערוך כניסה", "description": "Header for edit login item type" }, "editItemHeaderCard": { - "message": "Edit Card", + "message": "ערוך כרטיס", "description": "Header for edit card item type" }, "editItemHeaderIdentity": { - "message": "Edit Identity", + "message": "ערוך זהות", "description": "Header for edit identity item type" }, "editItemHeaderNote": { - "message": "Edit Note", + "message": "ערוך הערה", "description": "Header for edit note item type" }, "editItemHeaderSshKey": { - "message": "Edit SSH key", + "message": "ערוך מפתח SSH", "description": "Header for edit SSH key item type" }, "editItemHeaderTextSend": { - "message": "Edit Text Send", + "message": "ערוך סֵנְד של טקסט", "description": "Header for edit text send" }, "editItemHeaderFileSend": { - "message": "Edit File Send", + "message": "ערוך סֵנְד של קובץ", "description": "Header for edit file send" }, "viewItemHeaderLogin": { - "message": "View Login", + "message": "הצג כניסה", "description": "Header for view login item type" }, "viewItemHeaderCard": { - "message": "View Card", + "message": "הצג כרטיס", "description": "Header for view card item type" }, "viewItemHeaderIdentity": { - "message": "View Identity", + "message": "הצג זהות", "description": "Header for view identity item type" }, "viewItemHeaderNote": { - "message": "View Note", + "message": "הצג הערה", "description": "Header for view note item type" }, "viewItemHeaderSshKey": { - "message": "View SSH key", + "message": "הצג מפתח SSH", "description": "Header for view SSH key item type" }, "passwordHistory": { @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "הגדר סיסמה ראשית" }, @@ -3256,7 +3265,7 @@ } }, "exportingOrganizationVaultFromPasswordManagerWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported.", + "message": "רק הכספת הארגונית המשויכת עם $ORGANIZATION$ תיוצא.", "placeholders": { "organization": { "content": "$1", @@ -3265,7 +3274,7 @@ } }, "exportingOrganizationVaultFromAdminConsoleWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. My items collections will not be included.", + "message": "רק הכספת הארגונית המשויכת עם $ORGANIZATION$ תיוצא. פריטי האוספים שלי לא יכללו.", "placeholders": { "organization": { "content": "$1", @@ -3280,7 +3289,7 @@ "message": "שגיאת פענוח" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "שגיאה בקבלת נתוני מילוי אוטומטי" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden לא יכל לפענח את פריט(י) הכספת המפורט(ים) להלן." @@ -4054,13 +4063,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "לא ניתן למלא אוטומטית" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "ברירת המחדל להתאמה מוגדרת כ'התאמה מדויקת'. האתר הנוכחי אינו תואם באופן מדויק את פרטי הכניסה השמורים עבור פריט זה." }, "okay": { - "message": "Okay" + "message": "בסדר" }, "toggleSideNavigation": { "message": "החלף מצב ניווט צדדי" @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "ברירת מחדל ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "הצג זיהוי התאמה $WEBSITE$", "placeholders": { @@ -5586,7 +5605,7 @@ "message": "אפשרויות כספת" }, "emptyVaultDescription": { - "message": "הכספת מגינה על יותר מרק הסיסמאות שלך. אחסן כניסות מאובטחות, זהויות, כרטיסים והערות באופן מאובטח כאן." + "message": "הכספת מגנה על יותר מרק הסיסמאות שלך. אחסן כניסות מאובטחות, זהויות, כרטיסים והערות באופן מאובטח כאן." }, "introCarouselLabel": { "message": "ברוך בואך אל Bitwarden" @@ -5631,30 +5650,30 @@ "message": "ברוך בואך אל הכספת שלך!" }, "phishingPageTitleV2": { - "message": "Phishing attempt detected" + "message": "זוהה ניסיון דיוג" }, "phishingPageSummary": { - "message": "The site you are attempting to visit is a known malicious site and a security risk." + "message": "האתר שאתה מנסה לבקר הוא אתר זדוני ידוע וסכנת אבטחה." }, "phishingPageCloseTabV2": { - "message": "Close this tab" + "message": "סגור כרטיסיה זו" }, "phishingPageContinueV2": { - "message": "Continue to this site (not recommended)" + "message": "המשך לאתר זה (לא מומלץ)" }, "phishingPageExplanation1": { - "message": "This site was found in ", + "message": "אתר זה נמצא ב־", "description": "This is in multiple parts to allow for bold text in the middle of the sentence. A proper name follows this." }, "phishingPageExplanation2": { - "message": ", an open-source list of known phishing sites used for stealing personal and sensitive information.", + "message": ", רשימת קוד פתוח של אתרי דיוג ידועים המשמשים לגניבת מידע אישי ורגיש.", "description": "This is in multiple parts to allow for bold text in the middle of the sentence. A proper name precedes this." }, "phishingPageLearnMore": { - "message": "Learn more about phishing detection" + "message": "למד עוד על זיהוי דיוג" }, "protectedBy": { - "message": "Protected by $PRODUCT$", + "message": "מוגן על ידי $PRODUCT$", "placeholders": { "product": { "content": "$1", @@ -5767,16 +5786,58 @@ "message": "אשר דומיין של Key Connector" }, "atRiskLoginsSecured": { - "message": "Great job securing your at-risk logins!" + "message": "עבודה נהדרת באבטחת הכניסות בסיכון שלך!" + }, + "upgradeNow": { + "message": "שדרג עכשיו" + }, + "builtInAuthenticator": { + "message": "מאמת מובנה" + }, + "secureFileStorage": { + "message": "אחסון קבצים מאובטח" + }, + "emergencyAccess": { + "message": "גישת חירום" + }, + "breachMonitoring": { + "message": "ניטור פרצות" + }, + "andMoreFeatures": { + "message": "ועוד!" + }, + "planDescPremium": { + "message": "השלם אבטחה מקוונת" + }, + "upgradeToPremium": { + "message": "שדרג לפרימיום" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "טוען כספת" + }, + "vaultLoaded": { + "message": "הכספת נטענה" }, "settingDisabledByPolicy": { - "message": "This setting is disabled by your organization's policy.", + "message": "הגדרה זו מושבתת על ידי מדיניות של הארגון שלך.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." }, "zipPostalCodeLabel": { - "message": "ZIP / Postal code" + "message": "מיקוד" }, "cardNumberLabel": { - "message": "Card number" + "message": "מספר כרטיס" + }, + "sessionTimeoutSettingsAction": { + "message": "פעולת פסק זמן" } } diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 3172e767974..0af38bf6964 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On Locked" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On Restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "लॉन्च पर बायोमेट्रिक्स के लिए पूछें" }, - "premiumRequired": { - "message": "Premium Required" - }, - "premiumRequiredDesc": { - "message": "इस सुविधा का उपयोग करने के लिए प्रीमियम सदस्यता की आवश्यकता होती है।" - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom Environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "मास्टर पासवर्ड सेट करें" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 9e4c8d34004..9bb5ca08843 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -32,7 +32,7 @@ "message": "Jedinstvena prijava (SSO)" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "Tvoja organizacija zahtijeva jedinstvenu prijavu." }, "welcomeBack": { "message": "Dobro došli natrag" @@ -592,7 +592,10 @@ "message": "Prikaz" }, "viewAll": { - "message": "View all" + "message": "Vidi sve" + }, + "viewLess": { + "message": "View less" }, "viewLogin": { "message": "Prikaži prijavu" @@ -796,6 +799,12 @@ "onLocked": { "message": "Pri zaključavanju sustava" }, + "onIdle": { + "message": "U stanju besposlenosti" + }, + "onSleep": { + "message": "U stanju mirovanja sustava" + }, "onRestart": { "message": "Pri pokretanju preglednika" }, @@ -1035,10 +1044,10 @@ "message": "Stavka izmijenjena" }, "savedWebsite": { - "message": "Saved website" + "message": "Spremljeno mrežno mjesto" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "Spremljena mrežna mjesta ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Traži biometrijsku autentifikaciju pri pokretanju" }, - "premiumRequired": { - "message": "Potrebno premium članstvo" - }, - "premiumRequiredDesc": { - "message": "Za korištenje ove značajke potrebno je Premium članstvo." - }, "authenticationTimeout": { "message": "Istek vremena za autentifikaciju" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Moraš dodati ili osnovni URL poslužitelja ili barem jedno prilagođeno okruženje." }, + "selfHostedEnvMustUseHttps": { + "message": "URL mora koristiti HTTPS." + }, "customEnvironment": { "message": "Prilagođeno okruženje" }, @@ -1695,28 +1701,28 @@ "message": "Isključi auto-ispunu" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "Potvrdi auto-ispunu" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "Ova stranica ne odgovara tvojim spremljenim podacima za prijavu. Prije nego što uneseš svoje podatke za prijavu, provjeri je li riječ o pouzdanoj stranici." }, "showInlineMenuLabel": { "message": "Prikaži prijedloge auto-ispune na poljima obrazaca" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Kako Bitwarden štiti tvoje podatke od phishinga?" }, "currentWebsite": { - "message": "Current website" + "message": "Trenutna web stranica" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "Auto-ispuni i dodaj ovu stranicu" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "Auto-ispuni bez dodavanja" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "Nemoj auto-ispuniti" }, "showInlineMenuIdentitiesLabel": { "message": "Prikaži identitete kao prijedloge" @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Postavi glavnu lozinku" }, @@ -3280,7 +3289,7 @@ "message": "Pogreška pri dešifriranju" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "Greška kod dohvata podataka za auto-ispunu" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden nije mogao dešifrirati sljedeće stavke trezora." @@ -4054,13 +4063,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "Nije moguća auto-ispuna" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "Zadano podudaranje postavljeno je na „Točno podudaranje”. Trenutna web-stranica ne podudara se točno sa spremljenim podacima ove stavke za prijavu." }, "okay": { - "message": "Okay" + "message": "U redu" }, "toggleSideNavigation": { "message": "U/Isključi bočnu navigaciju" @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Zadano ($VALUE$)", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Prikaži otkrivanje podudaranja $WEBSITE$", "placeholders": { @@ -5769,14 +5788,56 @@ "atRiskLoginsSecured": { "message": "Rizične prijave su osigurane!" }, + "upgradeNow": { + "message": "Nadogradi sada" + }, + "builtInAuthenticator": { + "message": "Ugrađeni autentifikator" + }, + "secureFileStorage": { + "message": "Sigurna pohrana datoteka" + }, + "emergencyAccess": { + "message": "Pristup u nuždi" + }, + "breachMonitoring": { + "message": "Nadzor proboja" + }, + "andMoreFeatures": { + "message": "I više!" + }, + "planDescPremium": { + "message": "Dovrši online sigurnost" + }, + "upgradeToPremium": { + "message": " Nadogradi na Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Učitavanje trezora" + }, + "vaultLoaded": { + "message": "Trezor učitan" + }, "settingDisabledByPolicy": { "message": "Ova je postavka onemogućena pravilima tvoje organizacije.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." }, "zipPostalCodeLabel": { - "message": "ZIP / Postal code" + "message": "Poštanski broj" }, "cardNumberLabel": { - "message": "Card number" + "message": "Broj kartice" + }, + "sessionTimeoutSettingsAction": { + "message": "Radnja kod isteka" } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index a84487e5a1d..9b6a5d756d5 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -32,7 +32,7 @@ "message": "Egyszeri bejelentkezés használata" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "A szervezet egyszeri bejelentkezést igényel." }, "welcomeBack": { "message": "Üdvözlet újra" @@ -594,6 +594,9 @@ "viewAll": { "message": "Összes megtekintése" }, + "viewLess": { + "message": "kevesebb megjelenítése" + }, "viewLogin": { "message": "Bejelentkezés megtekintése" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Rendszerzároláskor" }, + "onIdle": { + "message": "Rendszer üresjárat esetén" + }, + "onSleep": { + "message": "Rendszer alvó mód esetén" + }, "onRestart": { "message": "Böngésző újraindításkor" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Biometria kérése indításkor" }, - "premiumRequired": { - "message": "Prémium funkció szükséges" - }, - "premiumRequiredDesc": { - "message": "Prémium tagság szükséges ennek a funkciónak eléréséhez a jövőben." - }, "authenticationTimeout": { "message": "Hitelesítési időkifutás" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Hozzá kell adni az alapszerver webcímét vagy legalább egy egyedi környezetet." }, + "selfHostedEnvMustUseHttps": { + "message": "A webcímeknek HTTPS-t kell használniuk." + }, "customEnvironment": { "message": "Egyedi környezet" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "Ez az oldal zavarja a Bitwarden élményt. Biztonsági intézkedésként ideiglenesen letiltásra került a Bitwarden belső menü." + }, "setMasterPassword": { "message": "Mesterjelszó beállítása" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Alapértelmezett ($VALUE$)", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "$WEBSITE$ egyező érzékelés megjelenítése", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Remek munka a kockázatos bejelentkezések biztosítása!" }, + "upgradeNow": { + "message": "Áttérés most" + }, + "builtInAuthenticator": { + "message": "Beépített hitelesítő" + }, + "secureFileStorage": { + "message": "Biztonságos fájl tárolás" + }, + "emergencyAccess": { + "message": "Sürgősségi hozzáférés" + }, + "breachMonitoring": { + "message": "Adatszivárgás figyelés" + }, + "andMoreFeatures": { + "message": "És még több!" + }, + "planDescPremium": { + "message": "Teljes körű online biztonság" + }, + "upgradeToPremium": { + "message": "Áttérés Prémium csomagra" + }, + "upgradeCompleteSecurity": { + "message": "Áttérés a teljes biztonságért" + }, + "premiumGivesMoreTools": { + "message": "A Premium több eszközt ad a biztonság megőrzéséhez, a hatékony munkavégzéshez és az irányítás megőrzéséhez." + }, + "explorePremium": { + "message": "Premium felfedezése" + }, + "loadingVault": { + "message": "Széf betöltése" + }, + "vaultLoaded": { + "message": "A széf betöltésre került." + }, "settingDisabledByPolicy": { "message": "Ezt a beállítást a szervezet házirendje letiltotta.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Kártya szám" + }, + "sessionTimeoutSettingsAction": { + "message": "Időkifutási művelet" } } diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index a94709a1be1..85fdfbf9afe 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Saat Komputer Terkunci" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Saat Peramban Dimulai Ulang" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Tanyakan untuk biometrik pada saat diluncurkan" }, - "premiumRequired": { - "message": "Membutuhkan Keanggotaan Premium" - }, - "premiumRequiredDesc": { - "message": "Keanggotaan premium diperlukan untuk menggunakan fitur ini." - }, "authenticationTimeout": { "message": "Batas waktu otentikasi" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Anda harus menambahkan antara URL dasar server atau paling tidak satu lingkungan ubahsuai." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Lingkungan Khusus" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Atur Kata Sandi Utama" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Tampilkan deteksi kecocokan $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 05cd6937246..a76bb05d15a 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Visualizza login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Al blocco del computer" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Al riavvio del browser" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Richiedi dati biometrici all'avvio" }, - "premiumRequired": { - "message": "Premium necessario" - }, - "premiumRequiredDesc": { - "message": "Passa a Premium per utilizzare questa funzionalità." - }, "authenticationTimeout": { "message": "Timeout autenticazione" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Devi aggiungere lo URL del server di base o almeno un ambiente personalizzato." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Ambiente personalizzato" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Imposta password principale" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Mostra corrispondenza $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "Questa impostazione è disabilitata dalle restrizioni della tua organizzazione.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 54405f69157..1294335481c 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -3,7 +3,7 @@ "message": "Bitwarden" }, "appLogoLabel": { - "message": "Bitwarden logo" + "message": "Bitwarden ロゴ" }, "extName": { "message": "Bitwarden パスワードマネージャー", @@ -32,7 +32,7 @@ "message": "シングルサインオンを使用する" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "あなたの組織ではシングルサインオン (SSO) を使用する必要があります。" }, "welcomeBack": { "message": "ようこそ" @@ -554,15 +554,15 @@ "message": "Reset search" }, "archiveNoun": { - "message": "Archive", + "message": "アーカイブ", "description": "Noun" }, "archiveVerb": { - "message": "Archive", + "message": "アーカイブ", "description": "Verb" }, "unArchive": { - "message": "Unarchive" + "message": "アーカイブ解除" }, "itemsInArchive": { "message": "Items in archive" @@ -594,8 +594,11 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { - "message": "View login" + "message": "ログイン情報を表示" }, "noItemsInList": { "message": "表示するアイテムがありません" @@ -796,6 +799,12 @@ "onLocked": { "message": "ロック時" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "ブラウザ再起動時" }, @@ -1035,10 +1044,10 @@ "message": "編集されたアイテム" }, "savedWebsite": { - "message": "Saved website" + "message": "保存されたウェブサイト" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "保存されたウェブサイト ($COUNT$ 件)", "placeholders": { "count": { "content": "$1", @@ -1145,10 +1154,10 @@ "description": "Tooltip and Aria label for edit button on cipher item" }, "newNotification": { - "message": "New notification" + "message": "新しい通知" }, "labelWithNotification": { - "message": "$LABEL$: New notification", + "message": "$LABEL$: 新しい通知", "description": "Label for the notification with a new login suggestion.", "placeholders": { "label": { @@ -1190,11 +1199,11 @@ "description": "User prompt to take action in order to save the login they just entered." }, "saveLogin": { - "message": "Save login", + "message": "ログインを保存", "description": "Prompt asking the user if they want to save their login details." }, "updateLogin": { - "message": "Update existing login", + "message": "既存のログイン情報を更新", "description": "Prompt asking the user if they want to update an existing login entry." }, "loginSaveSuccess": { @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "起動時に生体認証を要求する" }, - "premiumRequired": { - "message": "プレミアム会員専用" - }, - "premiumRequiredDesc": { - "message": "この機能を使うにはプレミアム会員になってください。" - }, "authenticationTimeout": { "message": "認証のタイムアウト" }, @@ -1567,13 +1570,13 @@ "message": "セキュリティキーの読み取り" }, "readingPasskeyLoading": { - "message": "Reading passkey..." + "message": "パスキーを読み込み中…" }, "passkeyAuthenticationFailed": { - "message": "Passkey authentication failed" + "message": "パスキー認証に失敗しました" }, "useADifferentLogInMethod": { - "message": "Use a different log in method" + "message": "別のログイン方法を使用する" }, "awaitingSecurityKeyInteraction": { "message": "セキュリティキーとの通信を待ち受け中…" @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "ベース サーバー URL または少なくとも 1 つのカスタム環境を追加する必要があります。" }, + "selfHostedEnvMustUseHttps": { + "message": "URL は HTTPS を使用する必要があります。" + }, "customEnvironment": { "message": "カスタム環境" }, @@ -1692,7 +1698,7 @@ } }, "turnOffAutofill": { - "message": "Turn off autofill" + "message": "自動入力をオフにする" }, "confirmAutofill": { "message": "Confirm autofill" @@ -1716,7 +1722,7 @@ "message": "Autofill without adding" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "自動入力しない" }, "showInlineMenuIdentitiesLabel": { "message": "ID を候補として表示する" @@ -1904,7 +1910,7 @@ "message": "セキュリティコード" }, "cardNumber": { - "message": "card number" + "message": "カード番号" }, "ex": { "message": "例:" @@ -2006,30 +2012,30 @@ "message": "SSH 鍵" }, "typeNote": { - "message": "Note" + "message": "メモ" }, "newItemHeaderLogin": { - "message": "New Login", + "message": "新規ログイン", "description": "Header for new login item type" }, "newItemHeaderCard": { - "message": "New Card", + "message": "新規カード", "description": "Header for new card item type" }, "newItemHeaderIdentity": { - "message": "New Identity", + "message": "新規身分証", "description": "Header for new identity item type" }, "newItemHeaderNote": { - "message": "New Note", + "message": "新規メモ", "description": "Header for new note item type" }, "newItemHeaderSshKey": { - "message": "New SSH key", + "message": "新しい SSH キー", "description": "Header for new SSH key item type" }, "newItemHeaderTextSend": { - "message": "New Text Send", + "message": "新しい Send テキスト", "description": "Header for new text send" }, "newItemHeaderFileSend": { @@ -2037,11 +2043,11 @@ "description": "Header for new file send" }, "editItemHeaderLogin": { - "message": "Edit Login", + "message": "ログインを編集", "description": "Header for edit login item type" }, "editItemHeaderCard": { - "message": "Edit Card", + "message": "カードを編集", "description": "Header for edit card item type" }, "editItemHeaderIdentity": { @@ -2049,15 +2055,15 @@ "description": "Header for edit identity item type" }, "editItemHeaderNote": { - "message": "Edit Note", + "message": "メモを編集", "description": "Header for edit note item type" }, "editItemHeaderSshKey": { - "message": "Edit SSH key", + "message": "SSH キーを編集", "description": "Header for edit SSH key item type" }, "editItemHeaderTextSend": { - "message": "Edit Text Send", + "message": "Send テキストを編集", "description": "Header for edit text send" }, "editItemHeaderFileSend": { @@ -2065,11 +2071,11 @@ "description": "Header for edit file send" }, "viewItemHeaderLogin": { - "message": "View Login", + "message": "ログインを表示", "description": "Header for view login item type" }, "viewItemHeaderCard": { - "message": "View Card", + "message": "カードを表示", "description": "Header for view card item type" }, "viewItemHeaderIdentity": { @@ -2077,11 +2083,11 @@ "description": "Header for view identity item type" }, "viewItemHeaderNote": { - "message": "View Note", + "message": "メモを表示", "description": "Header for view note item type" }, "viewItemHeaderSshKey": { - "message": "View SSH key", + "message": "SSH キーを表示", "description": "Header for view SSH key item type" }, "passwordHistory": { @@ -2340,7 +2346,7 @@ "message": "このパスワードを使用する" }, "useThisPassphrase": { - "message": "Use this passphrase" + "message": "このパスフレーズを使用" }, "useThisUsername": { "message": "このユーザー名を使用する" @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "マスターパスワードを設定" }, @@ -2657,7 +2666,7 @@ "message": "変更" }, "changePassword": { - "message": "Change password", + "message": "パスワードを変更", "description": "Change password button for browser at risk notification on login." }, "changeButtonTitle": { @@ -2670,7 +2679,7 @@ } }, "atRiskPassword": { - "message": "At-risk password" + "message": "リスクがあるパスワード" }, "atRiskPasswords": { "message": "リスクがあるパスワード" @@ -2846,7 +2855,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "maxAccessCountReached": { - "message": "Max access count reached", + "message": "最大アクセス数に達しました", "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "hideTextByDefault": { @@ -3196,7 +3205,7 @@ "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." }, "organizationName": { - "message": "Organization name" + "message": "組織名" }, "keyConnectorDomain": { "message": "Key Connector domain" @@ -3359,7 +3368,7 @@ "message": "サービス" }, "forwardedEmail": { - "message": "転送されたメールエイリアス" + "message": "転送されるメールエイリアス" }, "forwardedEmailDesc": { "message": "外部転送サービスを使用してメールエイリアスを生成します。" @@ -3608,7 +3617,7 @@ "message": "リクエストが送信されました" }, "loginRequestApprovedForEmailOnDevice": { - "message": "Login request approved for $EMAIL$ on $DEVICE$", + "message": "$EMAIL$ に $DEVICE$ でのログインを承認しました", "placeholders": { "email": { "content": "$1", @@ -3621,16 +3630,16 @@ } }, "youDeniedLoginAttemptFromAnotherDevice": { - "message": "You denied a login attempt from another device. If this was you, try to log in with the device again." + "message": "別のデバイスからのログイン試行を拒否しました。自分自身である場合は、もう一度デバイスでログインしてください。" }, "device": { - "message": "Device" + "message": "デバイス" }, "loginStatus": { - "message": "Login status" + "message": "ログイン状態" }, "masterPasswordChanged": { - "message": "Master password saved" + "message": "マスターパスワードが保存されました" }, "exposedMasterPassword": { "message": "流出したマスターパスワード" @@ -3723,28 +3732,28 @@ "message": "このデバイスを記憶して今後のログインをシームレスにする" }, "manageDevices": { - "message": "Manage devices" + "message": "デバイスを管理" }, "currentSession": { - "message": "Current session" + "message": "現在のセッション" }, "mobile": { - "message": "Mobile", + "message": "モバイル", "description": "Mobile app" }, "extension": { - "message": "Extension", + "message": "拡張機能", "description": "Browser extension/addon" }, "desktop": { - "message": "Desktop", + "message": "デスクトップ", "description": "Desktop app" }, "webVault": { - "message": "Web vault" + "message": "ウェブ保管庫" }, "webApp": { - "message": "Web app" + "message": "Web アプリ" }, "cli": { "message": "CLI" @@ -3754,22 +3763,22 @@ "description": "Software Development Kit" }, "requestPending": { - "message": "Request pending" + "message": "保留中のリクエスト" }, "firstLogin": { - "message": "First login" + "message": "初回ログイン" }, "trusted": { - "message": "Trusted" + "message": "信頼済み" }, "needsApproval": { - "message": "Needs approval" + "message": "承認が必要" }, "devices": { - "message": "Devices" + "message": "デバイス" }, "accessAttemptBy": { - "message": "Access attempt by $EMAIL$", + "message": "$EMAIL$ によるログインの試行", "placeholders": { "email": { "content": "$1", @@ -3778,31 +3787,31 @@ } }, "confirmAccess": { - "message": "Confirm access" + "message": "アクセスの確認" }, "denyAccess": { - "message": "Deny access" + "message": "アクセスを拒否" }, "time": { - "message": "Time" + "message": "時間" }, "deviceType": { - "message": "Device Type" + "message": "デバイス種別" }, "loginRequest": { - "message": "Login request" + "message": "ログインリクエスト" }, "thisRequestIsNoLongerValid": { - "message": "This request is no longer valid." + "message": "このリクエストは無効になりました。" }, "loginRequestHasAlreadyExpired": { - "message": "Login request has already expired." + "message": "ログインリクエストの有効期限が切れています。" }, "justNow": { - "message": "Just now" + "message": "たった今" }, "requestedXMinutesAgo": { - "message": "Requested $MINUTES$ minutes ago", + "message": "$MINUTES$ 分前に要求されました", "placeholders": { "minutes": { "content": "$1", @@ -3832,7 +3841,7 @@ "message": "管理者の承認を要求する" }, "unableToCompleteLogin": { - "message": "Unable to complete login" + "message": "ログインを完了できません" }, "loginOnTrustedDeviceOrAskAdminToAssignPassword": { "message": "You need to log in on a trusted device or ask your administrator to assign you a password." @@ -3902,13 +3911,13 @@ "message": "Trust organization" }, "trust": { - "message": "Trust" + "message": "信頼する" }, "doNotTrust": { - "message": "Do not trust" + "message": "信頼しない" }, "organizationNotTrusted": { - "message": "Organization is not trusted" + "message": "組織は信頼されていません" }, "emergencyAccessTrustWarning": { "message": "For the security of your account, only confirm if you have granted emergency access to this user and their fingerprint matches what is displayed in their account" @@ -3923,11 +3932,11 @@ "message": "Trust user" }, "sendsTitleNoItems": { - "message": "Send sensitive information safely", + "message": "機密情報を安全に送信", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsBodyNoItems": { - "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", + "message": "どのプラットフォームでも、誰とでも安全にファイルとデータを共有できます。流出を防止しながら、あなたの情報はエンドツーエンドで暗号化されます。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "inputRequired": { @@ -4054,13 +4063,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "自動入力できません" }, "cannotAutofillExactMatch": { "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." }, "okay": { - "message": "Okay" + "message": "OK" }, "toggleSideNavigation": { "message": "サイドナビゲーションの切り替え" @@ -4271,10 +4280,10 @@ "message": "コレクションを選択" }, "importTargetHintCollection": { - "message": "Select this option if you want the imported file contents moved to a collection" + "message": "インポートしたファイルコンテンツをコレクションに移動したい場合は、このオプションを選択してください" }, "importTargetHintFolder": { - "message": "Select this option if you want the imported file contents moved to a folder" + "message": "インポートしたファイルコンテンツをフォルダーに移動したい場合は、このオプションを選択してください" }, "importUnassignedItemsError": { "message": "割り当てられていないアイテムがファイルに含まれています。" @@ -4511,7 +4520,7 @@ "description": "Label indicating the most common import formats" }, "uriMatchDefaultStrategyHint": { - "message": "URI match detection is how Bitwarden identifies autofill suggestions.", + "message": "URI の一致検出方法は、Bitwarden が自動入力候補をどのように判別するかを指定します。", "description": "Explains to the user that URI match detection determines how Bitwarden suggests autofill options, and clarifies that this default strategy applies when no specific match detection is set for a login item." }, "regExAdvancedOptionWarning": { @@ -4527,7 +4536,7 @@ "description": "Link to match detection docs on warning dialog for advance match strategy" }, "uriAdvancedOption": { - "message": "Advanced options", + "message": "高度な設定", "description": "Advanced option placeholder for uri option component" }, "confirmContinueToBrowserSettingsTitle": { @@ -4714,7 +4723,7 @@ } }, "copyFieldCipherName": { - "message": "Copy $FIELD$, $CIPHERNAME$", + "message": "$FIELD$ ($CIPHERNAME$) をコピー", "description": "Title for a button that copies a field value to the clipboard.", "placeholders": { "field": { @@ -4861,31 +4870,31 @@ } }, "downloadBitwarden": { - "message": "Download Bitwarden" + "message": "Bitwarden をダウンロード" }, "downloadBitwardenOnAllDevices": { - "message": "Download Bitwarden on all devices" + "message": "すべてのデバイスに Bitwarden をダウンロード" }, "getTheMobileApp": { - "message": "Get the mobile app" + "message": "モバイルアプリを入手" }, "getTheMobileAppDesc": { "message": "Access your passwords on the go with the Bitwarden mobile app." }, "getTheDesktopApp": { - "message": "Get the desktop app" + "message": "デスクトップアプリを入手" }, "getTheDesktopAppDesc": { "message": "Access your vault without a browser, then set up unlock with biometrics to expedite unlocking in both the desktop app and browser extension." }, "downloadFromBitwardenNow": { - "message": "Download from bitwarden.com now" + "message": "bitwarden.com から今すぐダウンロード" }, "getItOnGooglePlay": { - "message": "Get it on Google Play" + "message": "Google Play で入手" }, "downloadOnTheAppStore": { - "message": "Download on the App Store" + "message": "App Store からダウンロード" }, "permanentlyDeleteAttachmentConfirmation": { "message": "この添付ファイルを完全に削除してもよろしいですか?" @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "既定 ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "一致検出 $WEBSITE$を表示", "placeholders": { @@ -5241,7 +5260,7 @@ "message": "拡張機能アイコンにログイン自動入力の候補の数を表示する" }, "accountAccessRequested": { - "message": "Account access requested" + "message": "アカウントへのアクセスが要求されました" }, "confirmAccessAttempt": { "message": "Confirm access attempt for $EMAIL$", @@ -5358,7 +5377,7 @@ "message": "Unlock PIN set" }, "unlockWithBiometricSet": { - "message": "Unlock with biometrics set" + "message": "生体認証でロック解除を設定しました" }, "authenticating": { "message": "認証中" @@ -5372,7 +5391,7 @@ "description": "Notification message for when a password has been regenerated" }, "saveToBitwarden": { - "message": "Save to Bitwarden", + "message": "Bitwarden へ保存", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { @@ -5580,13 +5599,13 @@ "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, "missingWebsite": { - "message": "Missing website" + "message": "ウェブサイトがありません" }, "settingsVaultOptions": { "message": "保管庫オプション" }, "emptyVaultDescription": { - "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + "message": "保管庫はパスワードだけではなく、ログイン情報、ID、カード、メモを安全に保管できます。" }, "introCarouselLabel": { "message": "Bitwarden へようこそ" @@ -5616,19 +5635,19 @@ "message": "Bitwarden のモバイル、ブラウザ、デスクトップアプリでは、保存できるパスワード数やデバイス数に制限はありません。" }, "nudgeBadgeAria": { - "message": "1 notification" + "message": "1件の通知" }, "emptyVaultNudgeTitle": { - "message": "Import existing passwords" + "message": "既存のパスワードをインポート" }, "emptyVaultNudgeBody": { "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." }, "emptyVaultNudgeButton": { - "message": "Import now" + "message": "今すぐインポート" }, "hasItemsVaultNudgeTitle": { - "message": "Welcome to your vault!" + "message": "保管庫へようこそ!" }, "phishingPageTitleV2": { "message": "Phishing attempt detected" @@ -5637,7 +5656,7 @@ "message": "The site you are attempting to visit is a known malicious site and a security risk." }, "phishingPageCloseTabV2": { - "message": "Close this tab" + "message": "このタブを閉じる" }, "phishingPageContinueV2": { "message": "Continue to this site (not recommended)" @@ -5654,7 +5673,7 @@ "message": "Learn more about phishing detection" }, "protectedBy": { - "message": "Protected by $PRODUCT$", + "message": "$PRODUCT$ によって保護されています", "placeholders": { "product": { "content": "$1", @@ -5680,7 +5699,7 @@ "example": "Include a Website so this login appears as an autofill suggestion." }, "newLoginNudgeBodyBold": { - "message": "Website", + "message": "ウェブサイト", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, @@ -5708,20 +5727,20 @@ "message": "With notes, securely store sensitive data like banking or insurance details." }, "newSshNudgeTitle": { - "message": "Developer-friendly SSH access" + "message": "開発者フレンドリーの SSH アクセス" }, "newSshNudgeBodyOne": { - "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication.", + "message": "SSHエージェントにキーを登録することで、高速かつ暗号化された認証が可能になります。", "description": "Two part message", "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "newSshNudgeBodyTwo": { - "message": "Learn more about SSH agent", + "message": "SSH エージェントに関する詳細", "description": "Two part message", "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "generatorNudgeTitle": { - "message": "Quickly create passwords" + "message": "パスワードをすばやく作成" }, "generatorNudgeBodyOne": { "message": "Easily create strong and unique passwords by clicking on", @@ -5738,7 +5757,7 @@ "description": "Aria label for the body content of the generator nudge" }, "aboutThisSetting": { - "message": "About this setting" + "message": "この設定について" }, "permitCipherDetailsDescription": { "message": "Bitwarden will use saved login URIs to identify which icon or change password URL should be used to improve your experience. No information is collected or saved when you use this service." @@ -5751,13 +5770,13 @@ "description": "'WebAssembly' is a technical term and should not be translated." }, "showMore": { - "message": "Show more" + "message": "もっと見る" }, "showLess": { - "message": "Show less" + "message": "隠す" }, "next": { - "message": "Next" + "message": "次へ" }, "moreBreadcrumbs": { "message": "More breadcrumbs", @@ -5769,14 +5788,56 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "今すぐアップグレード" + }, + "builtInAuthenticator": { + "message": "認証機を内蔵" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "緊急アクセス" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "プレミアムにアップグレード" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." }, "zipPostalCodeLabel": { - "message": "ZIP / Postal code" + "message": "郵便番号" }, "cardNumberLabel": { - "message": "Card number" + "message": "カード番号" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 82f18caf79f..52e9fbc5229 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Premium required" - }, - "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index f160e9a8cfa..7c4dbaf85dc 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Premium required" - }, - "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index f1c9e0ee8ab..d2ca68a0108 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "ಸಿಸ್ಟಮ್ ಲಾಕ್‌ನಲ್ಲಿ" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "ಬ್ರೌಸರ್ ಮರುಪ್ರಾರಂಭದಲ್ಲಿ" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "ಪ್ರೀಮಿಯಂ ಅಗತ್ಯವಿದೆ" - }, - "premiumRequiredDesc": { - "message": "ಈ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಬಳಸಲು ಪ್ರೀಮಿಯಂ ಸದಸ್ಯತ್ವ ಅಗತ್ಯವಿದೆ." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "ಕಸ್ಟಮ್ ಪರಿಸರ" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "ಮಾಸ್ಟರ್ ಪಾಸ್ವರ್ಡ್ ಹೊಂದಿಸಿ" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index c5a414fc81f..c583e173d91 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "로그인 보기" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "시스템 잠금 시" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "브라우저 재시작 시" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "실행 시 생체 인증 요구하기" }, - "premiumRequired": { - "message": "프리미엄 멤버십 필요" - }, - "premiumRequiredDesc": { - "message": "이 기능을 사용하려면 프리미엄 멤버십이 필요합니다." - }, "authenticationTimeout": { "message": "인증 시간 초과" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "기본 서버 URL이나 최소한 하나의 사용자 지정 환경을 추가해야 합니다." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "사용자 지정 환경" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "마스터 비밀번호 설정" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "$WEBSITE$ 일치 인식 보이기", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index df55af589bf..45ee71f75dd 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Užrakinant sistemą" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Paleidus iš naujo naršyklę" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Paleidžiant patvirtinti biometrinius duomenis" }, - "premiumRequired": { - "message": "Premium reikalinga" - }, - "premiumRequiredDesc": { - "message": "Premium narystė reikalinga šiai funkcijai naudoti." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Individualizuota aplinka" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Pagrindinio slaptažodžio nustatymas" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 6ca99492c50..d7e4b5eea9c 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Apskatīt visu" }, + "viewLess": { + "message": "Skatīt mazāk" + }, "viewLogin": { "message": "Apskatīt pieteikšanās vienumu" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Pēc sistēmas aizslēgšanas" }, + "onIdle": { + "message": "Sistēmas dīkstāvē" + }, + "onSleep": { + "message": "Pēc sistēmas iemigšanas" + }, "onRestart": { "message": "Pēc pārlūka pārsāknēšanas" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Palaižot vaicāt biometriju" }, - "premiumRequired": { - "message": "Nepieciešams Premium" - }, - "premiumRequiredDesc": { - "message": "Ir nepieciešama Premium dalība, lai izmantotu šo iespēju." - }, "authenticationTimeout": { "message": "Autentificēšanās noildze" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Jāpievieno vai no servera pamata URL vai vismaz viena pielāgota vide." }, + "selfHostedEnvMustUseHttps": { + "message": "URL ir jābūt HTTPS." + }, "customEnvironment": { "message": "Pielāgota vide" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "Šī lapa traucē Bitwarden darbību. Bitwarden iekļautā izvēlne ir īslaicīgi atspējot kā drošības mērs." + }, "setMasterPassword": { "message": "Uzstādīt galveno paroli" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Noklusējums ($VALUE$)", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Rādīt atbilstības noteikšanu $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Labs darbs riskam pakļauto pieteikšanās vienumu drošības uzlabošanā!" }, + "upgradeNow": { + "message": "Uzlabot tagad" + }, + "builtInAuthenticator": { + "message": "Iebūvēts autentificētājs" + }, + "secureFileStorage": { + "message": "Droša datņu krātuve" + }, + "emergencyAccess": { + "message": "Ārkārtas piekļuve" + }, + "breachMonitoring": { + "message": "Noplūžu pārraudzīšana" + }, + "andMoreFeatures": { + "message": "Un vēl!" + }, + "planDescPremium": { + "message": "Pilnīga drošība tiešsaistē" + }, + "upgradeToPremium": { + "message": "Uzlabot uz Premium" + }, + "upgradeCompleteSecurity": { + "message": "Uzlabo pilnīgas drošības iegūšanai" + }, + "premiumGivesMoreTools": { + "message": "Premium sniedz vairāk rīku drošībai, darba ražīgumam un pārraudzībai." + }, + "explorePremium": { + "message": "Izpētīt Premium" + }, + "loadingVault": { + "message": "Ielādē glabātavu" + }, + "vaultLoaded": { + "message": "Glabātava ielādēta" + }, "settingDisabledByPolicy": { "message": "Šis iestatījums ir atspējots apvienības pamatnostādnēs.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Kartes numurs" + }, + "sessionTimeoutSettingsAction": { + "message": "Noildzes darbība" } } diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 75eeb54c176..6c022f0043f 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "സിസ്റ്റം ലോക്കിൽ" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "ബ്രൌസർ പുനരാരംഭിക്കുമ്പോൾ" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "പ്രീമിയം അംഗത്വം ആവശ്യമാണ്" - }, - "premiumRequiredDesc": { - "message": "ഈ സവിശേഷത ഉപയോഗിക്കുന്നതിന് പ്രീമിയം അംഗത്വം ആവശ്യമാണ്." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "ഇഷ്‌ടാനുസൃത എൻവിയോണ്മെന്റ്" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "പ്രാഥമിക പാസ്‌വേഡ് സജ്ജമാക്കുക" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 333dda2a2f8..d34d1c87971 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Premium required" - }, - "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index f160e9a8cfa..7c4dbaf85dc 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Premium required" - }, - "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 3d632a60d3c..11bd78ed56c 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Ved maskinlåsing" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Ved nettleseromstart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Spør om biometri ved oppstart" }, - "premiumRequired": { - "message": "Premium er påkrevd" - }, - "premiumRequiredDesc": { - "message": "Et Premium-medlemskap er påkrevd for å bruke denne funksjonen." - }, "authenticationTimeout": { "message": "Tidsavbrudd for autentisering" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Tilpasset miljø" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Angi hovedpassord" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index f160e9a8cfa..7c4dbaf85dc 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Premium required" - }, - "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index d413149bd18..8817e04b163 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Alles weergeven" }, + "viewLess": { + "message": "Minder weergeven" + }, "viewLogin": { "message": "Login bekijken" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Bij systeemvergrendeling" }, + "onIdle": { + "message": "Bij systeeminactiviteit" + }, + "onSleep": { + "message": "Bij slaapmodus" + }, "onRestart": { "message": "Bij herstart van de browser" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Vraag om biometrie bij opstarten" }, - "premiumRequired": { - "message": "Premium is vereist" - }, - "premiumRequiredDesc": { - "message": "Je hebt een Premium-abonnement nodig om deze functie te gebruiken." - }, "authenticationTimeout": { "message": "Authenticatie-timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Je moet de basis URL van de server of ten minste één aangepaste omgeving toevoegen." }, + "selfHostedEnvMustUseHttps": { + "message": "URL's moeten HTTPS gebruiken." + }, "customEnvironment": { "message": "Aangepaste omgeving" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "Deze pagina verstoort de Bitwarden-ervaring. Het inline-menu van Bitwarden is tijdelijk uitgeschakeld als veiligheidsmaatregel." + }, "setMasterPassword": { "message": "Hoofdwachtwoord instellen" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Standaard ($VALUE$)", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Overeenkomstdetectie weergeven $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Goed gedaan, je hebt je risicovolle inloggegevens verbeterd!" }, + "upgradeNow": { + "message": "Nu upgraden" + }, + "builtInAuthenticator": { + "message": "Ingebouwde authenticator" + }, + "secureFileStorage": { + "message": "Beveiligde bestandsopslag" + }, + "emergencyAccess": { + "message": "Noodtoegang" + }, + "breachMonitoring": { + "message": "Lek-monitoring" + }, + "andMoreFeatures": { + "message": "En meer!" + }, + "planDescPremium": { + "message": "Online beveiliging voltooien" + }, + "upgradeToPremium": { + "message": "Opwaarderen naar Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade voor volledige beveiliging" + }, + "premiumGivesMoreTools": { + "message": "Premium geeft je meer tools om veilig te blijven, efficiënt te werken en in controle te blijven." + }, + "explorePremium": { + "message": "Premium verkennen" + }, + "loadingVault": { + "message": "Kluis laden" + }, + "vaultLoaded": { + "message": "Kluis geladen" + }, "settingDisabledByPolicy": { "message": "Deze instelling is uitgeschakeld door het beleid van uw organisatie.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Kaartnummer" + }, + "sessionTimeoutSettingsAction": { + "message": "Time-out actie" } } diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index f160e9a8cfa..7c4dbaf85dc 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Premium required" - }, - "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index f160e9a8cfa..7c4dbaf85dc 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Premium required" - }, - "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 6c9bea95451..41f679aba50 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Pokaż dane logowania" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Po zablokowaniu urządzenia" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Po uruchomieniu przeglądarki" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Wymagaj odblokowania biometrią po uruchomieniu przeglądarki" }, - "premiumRequired": { - "message": "Konto premium jest wymagane" - }, - "premiumRequiredDesc": { - "message": "Konto premium jest wymagane, aby skorzystać z tej funkcji." - }, "authenticationTimeout": { "message": "Przekroczono limit czasu uwierzytelniania" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Musisz dodać podstawowy adres URL serwera lub co najmniej jedno niestandardowe środowisko." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Niestandardowe środowisko" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Ustaw hasło główne" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Pokaż wykrywanie dopasowania $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Numer karty" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 1496455e85b..9e5d2331744 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -32,7 +32,7 @@ "message": "Usar autenticação única" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "A sua organização requer o uso da autenticação única." }, "welcomeBack": { "message": "Boas-vindas de volta" @@ -59,13 +59,13 @@ "message": "Endereço de e-mail" }, "masterPass": { - "message": "Senha mestra" + "message": "Senha principal" }, "masterPassDesc": { - "message": "A senha mestra é a senha que você usa para acessar o seu cofre. É muito importante que você não esqueça sua senha mestra. Não há maneira de recuperar a senha caso você se esqueça." + "message": "A senha principal é a senha que você usa para acessar o seu cofre. É muito importante que você não esqueça sua senha principal. Não há maneira de recuperar a senha caso você se esqueça." }, "masterPassHintDesc": { - "message": "Uma dica de senha mestra pode ajudá-lo(a) a lembrá-lo(a) caso você esqueça." + "message": "Uma dica de senha principal pode ajudá-lo(a) a lembrá-lo(a) caso você esqueça." }, "masterPassHintText": { "message": "Se você esquecer sua senha, a dica de senha pode ser enviada ao seu e-mail. $CURRENT$/$MAXIMUM$ caracteres máximos.", @@ -81,10 +81,10 @@ } }, "reTypeMasterPass": { - "message": "Digite novamente a senha mestra" + "message": "Digite novamente a senha principal" }, "masterPassHint": { - "message": "Dica de Senha Mestra (opcional)" + "message": "Dica de senha principal (opcional)" }, "passwordStrengthScore": { "message": "Pontuação de força da senha $SCORE$", @@ -108,7 +108,7 @@ } }, "finishJoiningThisOrganizationBySettingAMasterPassword": { - "message": "Termine de juntar-se nessa organização definindo uma senha mestra." + "message": "Termine de juntar-se à organização definindo uma senha principal." }, "tab": { "message": "Aba" @@ -264,13 +264,13 @@ "message": "Solicitar dica" }, "requestPasswordHint": { - "message": "Dica da senha mestra" + "message": "Dica da senha principal" }, "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { "message": "Digite o endereço de e-mail da sua conta e dica da sua senha será enviada para você" }, "getMasterPasswordHint": { - "message": "Obter dica da senha mestra" + "message": "Obter dica da senha principal" }, "continue": { "message": "Continuar" @@ -291,7 +291,7 @@ "message": "Confirme a sua identidade para continuar." }, "changeMasterPassword": { - "message": "Alterar senha mestra" + "message": "Alterar senha principal" }, "continueToWebApp": { "message": "Continuar no aplicativo web?" @@ -312,7 +312,7 @@ "message": "Ajude outras pessoas a descobrirem se o Bitwarden é o que elas estão procurando. Visite a loja de extensões do seu navegador e deixe uma avaliação agora." }, "changeMasterPasswordOnWebConfirmation": { - "message": "Você pode alterar a sua senha mestra no aplicativo web do Bitwarden." + "message": "Você pode alterar a sua senha principal no aplicativo web do Bitwarden." }, "fingerprintPhrase": { "message": "Frase biométrica", @@ -592,7 +592,10 @@ "message": "Ver" }, "viewAll": { - "message": "View all" + "message": "Ver tudo" + }, + "viewLess": { + "message": "Ver menos" }, "viewLogin": { "message": "Ver credencial" @@ -737,10 +740,10 @@ } }, "invalidMasterPassword": { - "message": "Senha mestra inválida" + "message": "Senha principal inválida" }, "invalidMasterPasswordConfirmEmailAndHost": { - "message": "Senha mestre inválida. Confirme que seu e-mail está correto e sua conta foi criada em $HOST$.", + "message": "Senha principal inválida. Confirme que seu e-mail está correto e sua conta foi criada em $HOST$.", "placeholders": { "host": { "content": "$1", @@ -796,6 +799,12 @@ "onLocked": { "message": "Ao bloquear o sistema" }, + "onIdle": { + "message": "Quando o sistema ficar inativo" + }, + "onSleep": { + "message": "Quando o sistema hibernar" + }, "onRestart": { "message": "Ao reiniciar o navegador" }, @@ -806,16 +815,16 @@ "message": "Segurança" }, "confirmMasterPassword": { - "message": "Confirme a senha mestra" + "message": "Confirme a senha principal" }, "masterPassword": { - "message": "Senha mestra" + "message": "Senha principal" }, "masterPassImportant": { - "message": "Sua senha mestra não pode ser recuperada se você esquecê-la!" + "message": "Sua senha principal não pode ser recuperada se você esquecê-la!" }, "masterPassHintLabel": { - "message": "Dica da senha mestra" + "message": "Dica da senha principal" }, "errorOccurred": { "message": "Ocorreu um erro" @@ -827,13 +836,13 @@ "message": "Endereço de e-mail inválido." }, "masterPasswordRequired": { - "message": "A senha mestre é necessária." + "message": "A senha principal é necessária." }, "confirmMasterPasswordRequired": { - "message": "É necessário digitar a senha mestra novamente." + "message": "É necessário digitar a senha principal novamente." }, "masterPasswordMinlength": { - "message": "A senha mestra deve ter pelo menos $VALUE$ caracteres.", + "message": "A senha principal deve ter pelo menos $VALUE$ caracteres.", "description": "The Master Password must be at least a specific number of characters long.", "placeholders": { "value": { @@ -843,7 +852,7 @@ } }, "masterPassDoesntMatch": { - "message": "A confirmação da senha mestra não corresponde." + "message": "A confirmação da senha principal não corresponde." }, "newAccountCreated": { "message": "A sua nova conta foi criada! Agora você pode iniciar a sessão." @@ -861,7 +870,7 @@ "message": "Você pode fechar esta janela" }, "masterPassSent": { - "message": "Enviamos um e-mail com a dica da sua senha mestra." + "message": "Enviamos um e-mail com a dica da sua senha principal." }, "verificationCodeRequired": { "message": "O código de verificação é necessário." @@ -1035,10 +1044,10 @@ "message": "Item salvo" }, "savedWebsite": { - "message": "Saved website" + "message": "Site salvo" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "Sites salvos ( $COUNT$ )", "placeholders": { "count": { "content": "$1", @@ -1242,7 +1251,7 @@ "message": "Ao alterar sua senha, você precisará entrar com a sua senha nova. Sessões ativas em outros dispositivos serão desconectados dentro de uma hora." }, "accountRecoveryUpdateMasterPasswordSubtitle": { - "message": "Mude a sua senha mestre para completar a recuperação de conta." + "message": "Mude a sua senha principal para completar a recuperação de conta." }, "enableChangedPasswordNotification": { "message": "Pedir para atualizar credencial existente" @@ -1326,7 +1335,7 @@ "message": "Esta senha será usada para exportar e importar este arquivo" }, "accountRestrictedOptionDescription": { - "message": "Use a chave de criptografia da sua conta, derivada do nome de usuário e senha mestra da sua conta, para criptografar a exportação e restringir a importação para apenas a conta atual do Bitwarden." + "message": "Use a chave de criptografia da sua conta, derivada do nome de usuário e senha principal da sua conta, para criptografar a exportação e restringir a importação para apenas a conta atual do Bitwarden." }, "passwordProtectedOptionDescription": { "message": "Defina uma senha para criptografar a exportação e importá-la para qualquer conta do Bitwarden usando a senha para descriptografar." @@ -1361,7 +1370,7 @@ "message": "As chaves de criptografia são únicas para cada conta de usuário do Bitwarden, então você não pode importar um arquivo de exportação criptografado para uma conta diferente." }, "exportMasterPassword": { - "message": "Insira a sua senha mestra para exportar os dados do seu cofre." + "message": "Insira a sua senha principal para exportar os dados do seu cofre." }, "shared": { "message": "Compartilhado" @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Pedir biometria ao abrir" }, - "premiumRequired": { - "message": "Requer Assinatura Premium" - }, - "premiumRequiredDesc": { - "message": "Uma assinatura Premium é necessária para usar esse recurso." - }, "authenticationTimeout": { "message": "Tempo de autenticação esgotado" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Você deve adicionar um URL do servidor de base ou pelo menos um ambiente personalizado." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs devem usar HTTPS." + }, "customEnvironment": { "message": "Ambiente personalizado" }, @@ -1695,28 +1701,28 @@ "message": "Desativar o preenchimento automático" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "Confirmar preenchimento automático" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "Esse site não corresponde aos detalhes salvos na credencial. Antes de preencher suas credenciais de acesso, certifique-se de que é um site confiável." }, "showInlineMenuLabel": { "message": "Mostrar sugestões de preenchimento automático em campos de formulário" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Como que o Bitwarden protege seus dados de phishing?" }, "currentWebsite": { - "message": "Current website" + "message": "Site atual" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "Preencher automaticamente e adicionar este site" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "Preencher automaticamente sem adicionar" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "Não preencher automaticamente" }, "showInlineMenuIdentitiesLabel": { "message": "Exibir identidades como sugestões" @@ -2267,10 +2273,10 @@ "description": "ex. A weak password. Scale: Weak -> Good -> Strong" }, "weakMasterPassword": { - "message": "Senha mestra fraca" + "message": "Senha principal Fraca" }, "weakMasterPasswordDesc": { - "message": "A senha mestra que você selecionou está fraca. Você deve usar uma senha mestra forte (ou uma frase-passe) para proteger a sua conta Bitwarden adequadamente. Tem certeza que deseja usar esta senha mestra?" + "message": "A senha principal que você selecionou está fraca. Você deve usar uma senha principal forte (ou uma frase-passe) para proteger a sua conta Bitwarden adequadamente. Tem certeza que deseja usar esta senha principal?" }, "pin": { "message": "PIN", @@ -2304,7 +2310,7 @@ "message": "Desbloquear com a biometria" }, "unlockWithMasterPassword": { - "message": "Desbloquear com senha mestra" + "message": "Desbloquear com senha principal" }, "awaitDesktop": { "message": "Aguardando confirmação do desktop" @@ -2313,10 +2319,10 @@ "message": "Confirme o uso de biometria no aplicativo do Bitwarden Desktop para ativar a biometria para o navegador." }, "lockWithMasterPassOnRestart": { - "message": "Bloquear com senha mestra ao reiniciar o navegador" + "message": "Bloquear com senha principal ao reiniciar o navegador" }, "lockWithMasterPassOnRestart1": { - "message": "Exigir senha mestra ao reiniciar o navegador" + "message": "Exigir senha principal ao reiniciar o navegador" }, "selectOneCollection": { "message": "Você deve selecionar pelo menos uma coleção." @@ -2430,20 +2436,23 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { - "message": "Definir senha mestra" + "message": "Definir senha principal" }, "currentMasterPass": { - "message": "Senha mestra atual" + "message": "Senha principal atual" }, "newMasterPass": { - "message": "Nova senha mestra" + "message": "Nova senha principal" }, "confirmNewMasterPass": { - "message": "Confirmar nova senha mestra" + "message": "Confirmar nova senha principal" }, "masterPasswordPolicyInEffect": { - "message": "Uma ou mais políticas da organização exigem que a sua senha mestra cumpra aos seguintes requisitos:" + "message": "Uma ou mais políticas da organização exigem que a sua senha principal cumpra aos seguintes requisitos:" }, "policyInEffectMinComplexity": { "message": "Pontuação mínima de complexidade de $SCORE$", @@ -2482,7 +2491,7 @@ } }, "masterPasswordPolicyRequirementsNotMet": { - "message": "A sua nova senha mestra não cumpre aos requisitos da política." + "message": "A sua nova senha principal não cumpre aos requisitos da política." }, "receiveMarketingEmailsV2": { "message": "Receba conselhos, novidades, e oportunidades de pesquisa do Bitwarden em sua caixa de entrada." @@ -2596,7 +2605,7 @@ "message": "Biometria falhou" }, "biometricsFailedDesc": { - "message": "A biometria não pode ser concluída, considere usar uma senha mestra ou desconectar. Se isso persistir, entre em contato com o suporte do Bitwarden." + "message": "A biometria não pode ser concluída, considere usar uma senha principal ou desconectar. Se isso persistir, entre em contato com o suporte do Bitwarden." }, "nativeMessaginPermissionErrorTitle": { "message": "Permissão não fornecida" @@ -3034,13 +3043,13 @@ "message": "Oculte seu endereço de e-mail dos visualizadores." }, "passwordPrompt": { - "message": "Solicitação nova de senha mestra" + "message": "Solicitação nova de senha principal" }, "passwordConfirmation": { - "message": "Confirmação de senha mestra" + "message": "Confirmação de senha principal" }, "passwordConfirmationDesc": { - "message": "Esta ação está protegida. Para continuar, por favor, reinsira a sua senha mestra para verificar sua identidade." + "message": "Esta ação está protegida. Para continuar, por favor, reinsira a sua senha principal para verificar sua identidade." }, "emailVerificationRequired": { "message": "Verificação de e-mail necessária" @@ -3052,28 +3061,28 @@ "message": "Você precisa verificar o seu e-mail para usar este recurso. Você pode verificar seu e-mail no cofre web." }, "masterPasswordSuccessfullySet": { - "message": "Senha mestra definida com sucesso" + "message": "Senha principal definida com sucesso" }, "updatedMasterPassword": { - "message": "Senha mestra atualizada" + "message": "Senha principal atualizada" }, "updateMasterPassword": { - "message": "Atualizar senha mestra" + "message": "Atualizar senha principal" }, "updateMasterPasswordWarning": { - "message": "Sua senha mestra foi alterada recentemente por um administrador de sua organização. Para acessar o cofre, você precisa atualizá-la agora. O processo desconectará você da sessão atual, exigindo que você entre novamente. Sessões ativas em outros dispositivos podem continuar ativas por até uma hora." + "message": "Sua senha principal foi alterada recentemente por um administrador de sua organização. Para acessar o cofre, você precisa atualizá-la agora. O processo desconectará você da sessão atual, exigindo que você entre novamente. Sessões ativas em outros dispositivos podem continuar ativas por até uma hora." }, "updateWeakMasterPasswordWarning": { - "message": "A sua senha mestra não atende a uma ou mais das políticas da sua organização. Para acessar o cofre, você deve atualizar a sua senha mestra agora. O processo desconectará você da sessão atual, exigindo que você entre novamente. Sessões ativas em outros dispositivos podem continuar ativas por até uma hora." + "message": "A sua senha principal não atende a uma ou mais das políticas da sua organização. Para acessar o cofre, você deve atualizar a sua senha principal agora. O processo desconectará você da sessão atual, exigindo que você entre novamente. Sessões ativas em outros dispositivos podem continuar ativas por até uma hora." }, "tdeDisabledMasterPasswordRequired": { - "message": "Sua organização desativou a criptografia confiável do dispositivo. Defina uma senha mestra para acessar o seu cofre." + "message": "Sua organização desativou a criptografia confiável do dispositivo. Defina uma senha principal para acessar o seu cofre." }, "resetPasswordPolicyAutoEnroll": { "message": "Inscrição automática" }, "resetPasswordAutoEnrollInviteWarning": { - "message": "Esta organização possui uma política empresarial que irá inscrevê-lo automaticamente na redefinição de senha. A inscrição permitirá que os administradores da organização alterem sua senha mestra." + "message": "Esta organização possui uma política empresarial que irá inscrevê-lo automaticamente na redefinição de senha. A inscrição permitirá que os administradores da organização alterem sua senha principal." }, "selectFolder": { "message": "Selecionar pasta..." @@ -3083,11 +3092,11 @@ "description": "Used as a message within the notification bar when no folders are found" }, "orgPermissionsUpdatedMustSetPassword": { - "message": "As permissões da sua organização foram atualizadas, exigindo que você defina uma senha mestra.", + "message": "As permissões da sua organização foram atualizadas, exigindo que você defina uma senha principal.", "description": "Used as a card title description on the set password page to explain why the user is there" }, "orgRequiresYouToSetPassword": { - "message": "Sua organização requer que você defina uma senha mestra.", + "message": "Sua organização requer que você defina uma senha principal.", "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { @@ -3193,7 +3202,7 @@ "message": "Nenhum identificador exclusivo encontrado." }, "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Uma senha mestra não é mais necessária para os membros da seguinte organização. Confirme o domínio abaixo com o administrador da sua organização." + "message": "Uma senha principal não é mais necessária para os membros da seguinte organização. Confirme o domínio abaixo com o administrador da sua organização." }, "organizationName": { "message": "Nome da organização" @@ -3205,10 +3214,10 @@ "message": "Sair da organização" }, "removeMasterPassword": { - "message": "Remover senha mestra" + "message": "Remover senha principal" }, "removedMasterPassword": { - "message": "Senha mestra removida" + "message": "Senha principal removida" }, "leaveOrganizationConfirmation": { "message": "Você tem certeza que deseja sair desta organização?" @@ -3280,7 +3289,7 @@ "message": "Erro ao descriptografar" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "Erro ao obter dados de preenchimento automático" }, "couldNotDecryptVaultItemsBelow": { "message": "O Bitwarden não conseguiu descriptografar o(s) item(ns) do cofre listado abaixo." @@ -3557,7 +3566,7 @@ } }, "loginWithMasterPassword": { - "message": "Entrar com a senha mestra" + "message": "Entrar com a senha principal" }, "newAroundHere": { "message": "Novo por aqui?" @@ -3630,16 +3639,16 @@ "message": "Estado de autenticação" }, "masterPasswordChanged": { - "message": "Senha mestre salva" + "message": "Senha principal salva" }, "exposedMasterPassword": { - "message": "Senha mestra comprometida" + "message": "Senha principal comprometida" }, "exposedMasterPasswordDesc": { "message": "A senha foi encontrada em um vazamento de dados. Use uma senha única para proteger sua conta. Tem certeza de que deseja usar uma senha já exposta?" }, "weakAndExposedMasterPassword": { - "message": "Senha mestra fraca e comprometida" + "message": "Senha principal fraca e comprometida" }, "weakAndBreachedMasterPasswordDesc": { "message": "Senha fraca identificada e encontrada em um vazamento de dados. Use uma senha forte e única para proteger a sua conta. Tem certeza de que deseja usar essa senha?" @@ -3651,7 +3660,7 @@ "message": "Importante:" }, "masterPasswordHint": { - "message": "Sua senha mestra não pode ser recuperada se você a esquecer!" + "message": "Sua senha principal não pode ser recuperada se você a esquecer!" }, "characterMinimum": { "message": "Mínimo de $LENGTH$ caracteres", @@ -4054,13 +4063,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "Não é possível preencher automaticamente" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "A correspondência padrão está configurada como 'Correspondência exata'. O site atual não corresponde exatamente aos detalhes salvos de credencial para este item." }, "okay": { - "message": "Okay" + "message": "Ok" }, "toggleSideNavigation": { "message": "Habilitar navegação lateral" @@ -4208,7 +4217,7 @@ "message": "Precisa de um método diferente?" }, "useMasterPassword": { - "message": "Usar a senha mestra" + "message": "Usar a senha principal" }, "usePin": { "message": "Usar PIN" @@ -4422,7 +4431,7 @@ "message": "Código" }, "lastPassMasterPassword": { - "message": "Senha mestra do LastPass" + "message": "Senha principal do LastPass" }, "lastPassAuthRequired": { "message": "Autenticação do LastPass necessária" @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Padrão ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Mostrar detecção de correspondência $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Ótimo trabalho protegendo suas credenciais em risco!" }, + "upgradeNow": { + "message": "Faça upgrade agora" + }, + "builtInAuthenticator": { + "message": "Autenticador integrado" + }, + "secureFileStorage": { + "message": "Armazenamento seguro de arquivos" + }, + "emergencyAccess": { + "message": "Acesso de emergência" + }, + "breachMonitoring": { + "message": "Monitoramento de brechas" + }, + "andMoreFeatures": { + "message": "E mais!" + }, + "planDescPremium": { + "message": "Segurança on-line completa" + }, + "upgradeToPremium": { + "message": "Faça upgrade para o Premium" + }, + "upgradeCompleteSecurity": { + "message": "Faça upgrade para segurança completa" + }, + "premiumGivesMoreTools": { + "message": "O Premium te oferece mais ferramentas para se permanecer seguro, trabalhar eficientemente, e manter o controle." + }, + "explorePremium": { + "message": "Explorar o Premium" + }, + "loadingVault": { + "message": "Carregando cofre" + }, + "vaultLoaded": { + "message": "Cofre carregado" + }, "settingDisabledByPolicy": { "message": "Essa configuração está desativada pela política da sua organização.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Número do cartão" + }, + "sessionTimeoutSettingsAction": { + "message": "Ação do tempo limite" } } diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index d3acb309860..4fd291e5c89 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Ver tudo" }, + "viewLess": { + "message": "Ver menos" + }, "viewLogin": { "message": "Ver credencial" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Ao bloquear o sistema" }, + "onIdle": { + "message": "Na inatividade do sistema" + }, + "onSleep": { + "message": "Na suspensão do sistema" + }, "onRestart": { "message": "Ao reiniciar o navegador" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Pedir biometria ao iniciar" }, - "premiumRequired": { - "message": "É necessária uma subscrição Premium" - }, - "premiumRequiredDesc": { - "message": "É necessária uma subscrição Premium para utilizar esta funcionalidade." - }, "authenticationTimeout": { "message": "Tempo limite de autenticação" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Deve adicionar o URL do servidor de base ou pelo menos um ambiente personalizado." }, + "selfHostedEnvMustUseHttps": { + "message": "Os URLs devem usar HTTPS." + }, "customEnvironment": { "message": "Ambiente personalizado" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "Esta página está a interferir com a experiência do Bitwarden. O menu em linha do Bitwarden foi temporariamente desativado como medida de segurança." + }, "setMasterPassword": { "message": "Definir a palavra-passe mestra" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Predefinido ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Mostrar deteção de correspondência para $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Excelente trabalho ao proteger as suas credenciais em risco!" }, + "upgradeNow": { + "message": "Atualizar agora" + }, + "builtInAuthenticator": { + "message": "Autenticador incorporado" + }, + "secureFileStorage": { + "message": "Armazenamento seguro de ficheiros" + }, + "emergencyAccess": { + "message": "Acesso de emergência" + }, + "breachMonitoring": { + "message": "Monitorização de violações" + }, + "andMoreFeatures": { + "message": "E muito mais!" + }, + "planDescPremium": { + "message": "Segurança total online" + }, + "upgradeToPremium": { + "message": "Atualizar para o Premium" + }, + "upgradeCompleteSecurity": { + "message": "Atualize para obter segurança total" + }, + "premiumGivesMoreTools": { + "message": "O Premium oferece mais ferramentas para manter a segurança, trabalhar com eficiência e manter o controlo." + }, + "explorePremium": { + "message": "Explorar o Premium" + }, + "loadingVault": { + "message": "A carregar o cofre" + }, + "vaultLoaded": { + "message": "Cofre carregado" + }, "settingDisabledByPolicy": { "message": "Esta configuração está desativada pela política da sua organização.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Número do cartão" + }, + "sessionTimeoutSettingsAction": { + "message": "Ação de tempo limite" } } diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 0206f473448..66a5b9d796b 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "La blocarea sistemului" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "La repornirea browserului" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Solicitați date biometrice la pornire" }, - "premiumRequired": { - "message": "Premium necesar" - }, - "premiumRequiredDesc": { - "message": "Pentru a utiliza această funcție este necesar un abonament Premium." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Mediu personalizat" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Setare parolă principală" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index b69494d472e..1f3d7c7234f 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Посмотреть все" }, + "viewLess": { + "message": "Свернуть" + }, "viewLogin": { "message": "Просмотр логина" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Вместе с компьютером" }, + "onIdle": { + "message": "При бездействии" + }, + "onSleep": { + "message": "В режиме сна" + }, "onRestart": { "message": "При перезапуске браузера" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Запрашивать биометрию при запуске" }, - "premiumRequired": { - "message": "Требуется Премиум" - }, - "premiumRequiredDesc": { - "message": "Для использования этой функции необходим Премиум." - }, "authenticationTimeout": { "message": "Таймаут аутентификации" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Вы должны добавить либо базовый URL сервера, либо хотя бы одно пользовательское окружение." }, + "selfHostedEnvMustUseHttps": { + "message": "URL должны использовать HTTPS." + }, "customEnvironment": { "message": "Пользовательское окружение" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "Эта страница мешает работе Bitwarden. Встроенное меню Bitwarden было временно отключено в целях безопасности." + }, "setMasterPassword": { "message": "Задать мастер-пароль" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "По умолчанию ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Показать обнаружение совпадений $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Отличная работа по защите ваших логинов, подверженных риску!" }, + "upgradeNow": { + "message": "Изменить сейчас" + }, + "builtInAuthenticator": { + "message": "Встроенный аутентификатор" + }, + "secureFileStorage": { + "message": "Защищенное хранилище файлов" + }, + "emergencyAccess": { + "message": "Экстренный доступ" + }, + "breachMonitoring": { + "message": "Мониторинг нарушений" + }, + "andMoreFeatures": { + "message": "И многое другое!" + }, + "planDescPremium": { + "message": "Полная онлайн-защищенность" + }, + "upgradeToPremium": { + "message": "Обновить до Премиум" + }, + "upgradeCompleteSecurity": { + "message": "Перейти для полной защищенности" + }, + "premiumGivesMoreTools": { + "message": "Премиум предоставит вам больше инструментов для обеспечения безопасности, эффективной работы и контроля над ситуацией." + }, + "explorePremium": { + "message": "Познакомиться с Премиум" + }, + "loadingVault": { + "message": "Загрузка хранилища" + }, + "vaultLoaded": { + "message": "Хранилище загружено" + }, "settingDisabledByPolicy": { "message": "Этот параметр отключен политикой вашей организации.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Номер карты" + }, + "sessionTimeoutSettingsAction": { + "message": "Тайм-аут действия" } } diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 60ce2436254..61dc029754a 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "පද්ධතිය ලොක් මත" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "බ්රව්සරය නැවත ආරම්භ" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "වාරික අවශ්ය" - }, - "premiumRequiredDesc": { - "message": "මෙම අංගය භාවිතා කිරීම සඳහා වාරික සාමාජිකත්වයක් අවශ්ය වේ." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "අභිරුචි පරිසරය" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "මාස්ටර් මුරපදය සකසන්න" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 46ff6837c70..865e832fda3 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Zobraziť všetky" }, + "viewLess": { + "message": "Zobraziť menej" + }, "viewLogin": { "message": "Zobraziť prihlásenie" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Keď je systém uzamknutý" }, + "onIdle": { + "message": "Pri nečinnosti systému" + }, + "onSleep": { + "message": "V režime spánku" + }, "onRestart": { "message": "Po reštarte prehliadača" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Pri spustení požiadať o biometriu" }, - "premiumRequired": { - "message": "Vyžaduje sa prémiový účet" - }, - "premiumRequiredDesc": { - "message": "Na použitie tejto funkcie je potrebné prémiové členstvo." - }, "authenticationTimeout": { "message": "Časový limit overenia" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Musíte pridať buď základnú adresu URL servera, alebo aspoň jedno vlastné prostredie." }, + "selfHostedEnvMustUseHttps": { + "message": "Adresy URL musia používať HTTPS." + }, "customEnvironment": { "message": "Vlastné prostredie" }, @@ -2189,7 +2195,7 @@ "description": "Default URI match detection for autofill." }, "toggleOptions": { - "message": "Voľby prepínača" + "message": "Zobraziť/skryť možnosti" }, "toggleCurrentUris": { "message": "Prepnúť zobrazenie aktuálnej URI", @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "Táto stránka narúša zážitok zo Bitwardenu. Inline ponuka Bitwardenu bola dočasne vypnutá ako bezpečnostné opatrenie." + }, "setMasterPassword": { "message": "Nastaviť hlavné heslo" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Predvolené ($VALUE$)", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Zobraziť zisťovanie zhody $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Skvelá práca pri zabezpečení vašich ohrozených prihlasovacích údajov!" }, + "upgradeNow": { + "message": "Upgradovať teraz" + }, + "builtInAuthenticator": { + "message": "Zabudovaný autentifikátor" + }, + "secureFileStorage": { + "message": "Bezpečné ukladanie súborov" + }, + "emergencyAccess": { + "message": "Núdzový prístup" + }, + "breachMonitoring": { + "message": "Sledovanie únikov" + }, + "andMoreFeatures": { + "message": "A ešte viac!" + }, + "planDescPremium": { + "message": "Úplné online zabezpečenie" + }, + "upgradeToPremium": { + "message": "Upgradovať na Prémium" + }, + "upgradeCompleteSecurity": { + "message": "Upgradovať pre úplné zabezpečenie" + }, + "premiumGivesMoreTools": { + "message": "Predplatné Prémium vám poskytuje viac nástrojov na zabezpečenie, efektívnu prácu a kontrolu." + }, + "explorePremium": { + "message": "Preskúmať Prémium" + }, + "loadingVault": { + "message": "Načítava sa trezor" + }, + "vaultLoaded": { + "message": "Trezor sa načítal" + }, "settingDisabledByPolicy": { "message": "Politika organizácie vypla toto nastavenie.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Číslo karty" + }, + "sessionTimeoutSettingsAction": { + "message": "Akcia pri vypršaní časového limitu" } } diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 53f7a9d8f03..ebb245290f9 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Ob zaklepu sistema" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Ob ponovnem zagonu brskalnika" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ob zagonu zahtevaj biometrično preverjanje" }, - "premiumRequired": { - "message": "Potrebno je premium članstvo" - }, - "premiumRequiredDesc": { - "message": "Premium članstvo je potrebno za uporabo te funkcije." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Okolje po meri" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Nastavi glavno geslo" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 169713c5047..d54a6ba928f 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -32,7 +32,7 @@ "message": "Употребити једнократну пријаву" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "Ваша организација захтева јединствену пријаву." }, "welcomeBack": { "message": "Добродошли назад" @@ -592,7 +592,10 @@ "message": "Приказ" }, "viewAll": { - "message": "View all" + "message": "Прегледај све" + }, + "viewLess": { + "message": "View less" }, "viewLogin": { "message": "Преглед пријаве" @@ -796,6 +799,12 @@ "onLocked": { "message": "На закључавање система" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "На покретање прегледача" }, @@ -1035,10 +1044,10 @@ "message": "Ставка уређена" }, "savedWebsite": { - "message": "Saved website" + "message": "Сачувана веб локација" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "Сачувана веб локација ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Захтевај биометрију при покретању" }, - "premiumRequired": { - "message": "Потребан Премијум" - }, - "premiumRequiredDesc": { - "message": "Премијум чланство је неопходно за употребу ове опције." - }, "authenticationTimeout": { "message": "Истекло је време аутентификације" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Морате додати или основни УРЛ сервера или бар једно прилагођено окружење." }, + "selfHostedEnvMustUseHttps": { + "message": "Везе морају да користе HTTPS." + }, "customEnvironment": { "message": "Прилагођено окружење" }, @@ -1695,28 +1701,28 @@ "message": "Угасити ауто-пуњење" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "Потврди аутопуњење" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "Овај сајт се не подудара са вашим сачуваним подацима за пријаву. Пре него што унесете своје акредитиве за пријаву, уверите се да је то поуздан сајт." }, "showInlineMenuLabel": { "message": "Прикажи предлоге за ауто-попуњавање у пољима обрасца" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Како Bitwarden штити ваше податке од фишинга?" }, "currentWebsite": { - "message": "Current website" + "message": "Тренутни сајт" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "Ауто-попуни и додај овај сајт" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "Ауто-попуни без додавања" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "Не попуни" }, "showInlineMenuIdentitiesLabel": { "message": "Приказати идентитете као предлоге" @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Постави Главну Лозинку" }, @@ -3280,7 +3289,7 @@ "message": "Грешка при декрипцији" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "Грешка при преузимању података за ауто-попуњавање" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden није могао да декриптује ставке из трезора наведене испод." @@ -4054,13 +4063,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "Не може да се ауто-попуни" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "Подразумевано подударање је подешено на „Тачно подударање“. Тренутна веб локација не одговара тачно сачуваним детаљима за пријаву за ову ставку." }, "okay": { - "message": "Okay" + "message": "У реду" }, "toggleSideNavigation": { "message": "Укључите бочну навигацију" @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Подразумевано ($VALUE$)", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Прикажи откривање подударања $WEBSITE$", "placeholders": { @@ -5767,16 +5786,58 @@ "message": "Потврдите домен конектора кључа" }, "atRiskLoginsSecured": { - "message": "Great job securing your at-risk logins!" + "message": "Сјајан посао обезбеђивања ваших ризичних пријава!" + }, + "upgradeNow": { + "message": "Надогради сада" + }, + "builtInAuthenticator": { + "message": "Уграђени аутентификатор" + }, + "secureFileStorage": { + "message": "Сигурно складиштење датотека" + }, + "emergencyAccess": { + "message": "Хитан приступ" + }, + "breachMonitoring": { + "message": "Праћење повreda безбедности" + }, + "andMoreFeatures": { + "message": "И још више!" + }, + "planDescPremium": { + "message": "Потпуна онлајн безбедност" + }, + "upgradeToPremium": { + "message": "Надоградите на Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" }, "settingDisabledByPolicy": { - "message": "This setting is disabled by your organization's policy.", + "message": "Ово подешавање је онемогућено смерницама ваше организације.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." }, "zipPostalCodeLabel": { - "message": "ZIP / Postal code" + "message": "ZIP/Поштански број" }, "cardNumberLabel": { - "message": "Card number" + "message": "Број картице" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 3b84369db47..245692a27aa 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -32,7 +32,7 @@ "message": "Använd Single Sign-On" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "Din organisation kräver single sign-on." }, "welcomeBack": { "message": "Välkommen tillbaka" @@ -87,7 +87,7 @@ "message": "Huvudlösenordsledtråd (valfri)" }, "passwordStrengthScore": { - "message": "Lösenordsstyrka $SCORE$ (score)", + "message": "Lösenordsstyrka $SCORE$", "placeholders": { "score": { "content": "$1", @@ -108,7 +108,7 @@ } }, "finishJoiningThisOrganizationBySettingAMasterPassword": { - "message": "Avsluta anslutningen till denna organisation genom att ange ett huvudlösenord." + "message": "Slutför anslutningen till den här organisationen genom att ställa in ett huvudlösenord." }, "tab": { "message": "Flik" @@ -592,7 +592,10 @@ "message": "Visa" }, "viewAll": { - "message": "View all" + "message": "Visa alla" + }, + "viewLess": { + "message": "Visa mindre" }, "viewLogin": { "message": "Visa inloggning" @@ -796,6 +799,12 @@ "onLocked": { "message": "Vid låsning av datorn" }, + "onIdle": { + "message": "När systemet är overksamt" + }, + "onSleep": { + "message": "När systemet är i strömsparläge" + }, "onRestart": { "message": "Vid omstart" }, @@ -1035,10 +1044,10 @@ "message": "Objekt sparat" }, "savedWebsite": { - "message": "Saved website" + "message": "Sparad webbplats" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "Sparade webbplatser ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Be om biometri vid start" }, - "premiumRequired": { - "message": "Premium krävs" - }, - "premiumRequiredDesc": { - "message": "Ett premium-medlemskap krävs för att använda den här funktionen." - }, "authenticationTimeout": { "message": "Timeout för autentisering" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Du måste lägga till antingen serverns bas-URL eller minst en anpassad miljö." }, + "selfHostedEnvMustUseHttps": { + "message": "Webbadresser måste använda HTTPS." + }, "customEnvironment": { "message": "Anpassad miljö" }, @@ -1695,28 +1701,28 @@ "message": "Stäng av autofyll" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "Bekräfta autofyll" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "Denna webbplats matchar inte dina sparade inloggningsuppgifter. Innan du fyller i dina inloggningsuppgifter, se till att det är en betrodd webbplats." }, "showInlineMenuLabel": { "message": "Visa förslag för autofyll i formulärfält" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Hur skyddar Bitwarden dina data från nätfiske?" }, "currentWebsite": { - "message": "Current website" + "message": "Aktuell webbplats" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "Autofyll och lägg till denna webbplats" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "Autofyll utan att lägga till" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "Autofyll inte" }, "showInlineMenuIdentitiesLabel": { "message": "Visa identiteter som förslag" @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "Denna sida stör Bitwarden-upplevelsen. Bitwardens inbyggda meny har tillfälligt inaktiverats som en säkerhetsåtgärd." + }, "setMasterPassword": { "message": "Ange huvudlösenord" }, @@ -3280,7 +3289,7 @@ "message": "Dekrypteringsfel" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "Fel vid hämtning av autofylldata" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden kunde inte dekryptera valvföremålet/valvföremålen som listas nedan." @@ -4054,13 +4063,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "Kan inte autofylla" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "Standardmatchning är satt till 'Exakt matchning'. Den aktuella webbplatsen matchar inte exakt de sparade inloggningsuppgifterna för detta objekt." }, "okay": { - "message": "Okay" + "message": "Okej" }, "toggleSideNavigation": { "message": "Växla sidonavigering" @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Standard ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Visa matchningsdetektering $WEBSITE", "placeholders": { @@ -5769,14 +5788,56 @@ "atRiskLoginsSecured": { "message": "Bra jobbat med att säkra upp dina inloggninar i riskzonen!" }, + "upgradeNow": { + "message": "Uppgradera nu" + }, + "builtInAuthenticator": { + "message": "Inbyggd autenticator" + }, + "secureFileStorage": { + "message": "Säker fillagring" + }, + "emergencyAccess": { + "message": "Nödåtkomst" + }, + "breachMonitoring": { + "message": "Intrångsmonitorering" + }, + "andMoreFeatures": { + "message": "och mer!" + }, + "planDescPremium": { + "message": "Komplett säkerhet online" + }, + "upgradeToPremium": { + "message": "Uppgradera till Premium" + }, + "upgradeCompleteSecurity": { + "message": "Uppgradera för fullständig säkerhet" + }, + "premiumGivesMoreTools": { + "message": "Premium ger dig fler verktyg för att hålla dig säker, arbeta effektivt och ha kontroll." + }, + "explorePremium": { + "message": "Utforska Premium" + }, + "loadingVault": { + "message": "Läser in valv" + }, + "vaultLoaded": { + "message": "Valvet lästes in" + }, "settingDisabledByPolicy": { "message": "Denna inställning är inaktiverad enligt din organisations policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." }, "zipPostalCodeLabel": { - "message": "ZIP / Postal code" + "message": "Postnummer" }, "cardNumberLabel": { - "message": "Card number" + "message": "Kortnummer" + }, + "sessionTimeoutSettingsAction": { + "message": "Tidsgränsåtgärd" } } diff --git a/apps/browser/src/_locales/ta/messages.json b/apps/browser/src/_locales/ta/messages.json index a72a4910ef8..a6e2ad0ee31 100644 --- a/apps/browser/src/_locales/ta/messages.json +++ b/apps/browser/src/_locales/ta/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "உள்நுழைவைக் காண்க" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "சிஸ்டம் பூட்டப்பட்டவுடன்" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "உலாவி மறுதொடக்கம் செய்யப்பட்டவுடன்" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "தொடங்கும் போது பயோமெட்ரிக்ஸைக் கேட்கவும்" }, - "premiumRequired": { - "message": "பிரீமியம் தேவை" - }, - "premiumRequiredDesc": { - "message": "இந்த அம்சத்தைப் பயன்படுத்த ஒரு பிரீமியம் மெம்பர்ஷிப் தேவை." - }, "authenticationTimeout": { "message": "அங்கீகரிப்பு டைம் அவுட்" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "நீங்கள் பேஸ் சர்வர் URL-ஐ அல்லது குறைந்தது ஒரு தனிப்பயன் சூழலைச் சேர்க்க வேண்டும்." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "தனிப்பயன் சூழல்" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "முதன்மை கடவுச்சொல்லை அமை" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "பொருத்தமான கண்டறிதலைக் காட்டு $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index f160e9a8cfa..7c4dbaf85dc 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Premium required" - }, - "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index dd27da81316..ff0c05a470a 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On Locked" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On Restart" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Ask for biometrics on launch" }, - "premiumRequired": { - "message": "Premium Required" - }, - "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." - }, "authenticationTimeout": { "message": "Authentication timeout" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom Environment" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "ตั้งรหัสผ่านหลัก" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Show match detection $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Upgrade now" + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "emergencyAccess": { + "message": "Emergency access" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "upgradeToPremium": { + "message": "Upgrade to Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index d982d0f3a1a..b2bff83e8a9 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -32,7 +32,7 @@ "message": "Çoklu oturum açma kullan" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "Kuruluşunuz çoklu oturum açma gerektiriyor." }, "welcomeBack": { "message": "Tekrar hoş geldiniz" @@ -594,6 +594,9 @@ "viewAll": { "message": "Tümünü göster" }, + "viewLess": { + "message": "Daha az göster" + }, "viewLogin": { "message": "Hesabı göster" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Sistem kilitlenince" }, + "onIdle": { + "message": "Sistem boştayken" + }, + "onSleep": { + "message": "Sistem uyuyunca" + }, "onRestart": { "message": "Tarayıcı yeniden başlatılınca" }, @@ -1035,10 +1044,10 @@ "message": "Hesap kaydedildi" }, "savedWebsite": { - "message": "Saved website" + "message": "Kayıtlı web sitesi" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "Kayıtlı web siteleri ( $COUNT$ )", "placeholders": { "count": { "content": "$1", @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Açılışta biyometri doğrulaması iste" }, - "premiumRequired": { - "message": "Premium gerekli" - }, - "premiumRequiredDesc": { - "message": "Bu özelliği kullanmak için premium üyelik gereklidir." - }, "authenticationTimeout": { "message": "Kimlik doğrulama zaman aşımı" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Temel Sunucu URL’sini veya en az bir özel ortam eklemelisiniz." }, + "selfHostedEnvMustUseHttps": { + "message": "URL'ler HTTPS kullanmalıdır." + }, "customEnvironment": { "message": "Özel ortam" }, @@ -1695,28 +1701,28 @@ "message": "Otomatik doldurmayı kapat" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "Otomatik doldurmayı onayla" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "Bu site kayıtlı hesap bilgilerinizle eşleşmiyor. Hesap bilgilerinizi doldurmadan önce sitenin güvenilir olduğundan emin olun." }, "showInlineMenuLabel": { "message": "Form alanlarında otomatik doldurma önerilerini göster" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Bitwarden verilerinizi kimlik avı saldırılarından nasıl koruyor?" }, "currentWebsite": { - "message": "Current website" + "message": "Geçerli web sitesi" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "Otomatik doldur ve bu siteyi ekle" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "Eklemeden otomatik doldur" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "Otomatik doldurma" }, "showInlineMenuIdentitiesLabel": { "message": "Kimlikleri öneri olarak göster" @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "Bu sayfa Bitwarden deneyimiyle çakışıyor. Güvenlik önlemi olarak Bitwarden satır içi menüsü geçici olarak devre dışı bırakıldı." + }, "setMasterPassword": { "message": "Ana parolayı belirle" }, @@ -3280,7 +3289,7 @@ "message": "Şifre çözme sorunu" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "Otomatik doldurma verileri alınırken hata oluştu" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden aşağıdaki kasa öğelerini deşifre edemedi." @@ -4054,13 +4063,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "Otomatik doldurulamıyor" }, "cannotAutofillExactMatch": { "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." }, "okay": { - "message": "Okay" + "message": "Tamam" }, "toggleSideNavigation": { "message": "Kenar menüsünü aç/kapat" @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Varsayılan ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "$WEBSITE$ eşleşme tespitini göster", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "Great job securing your at-risk logins!" }, + "upgradeNow": { + "message": "Şimdi yükselt" + }, + "builtInAuthenticator": { + "message": "Dahili kimlik doğrulayıcı" + }, + "secureFileStorage": { + "message": "Güvenli dosya depolama" + }, + "emergencyAccess": { + "message": "Acil durum erişimi" + }, + "breachMonitoring": { + "message": "İhlal izleme" + }, + "andMoreFeatures": { + "message": "Ve daha fazlası!" + }, + "planDescPremium": { + "message": "Eksiksiz çevrimiçi güvenlik" + }, + "upgradeToPremium": { + "message": "Premium'a yükselt" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Kasa yükleniyor" + }, + "vaultLoaded": { + "message": "Kasa yüklendi" + }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "Kart numarası" + }, + "sessionTimeoutSettingsAction": { + "message": "Zaman aşımı eylemi" } } diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index aa118c0b93e..b104f845fa7 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -32,7 +32,7 @@ "message": "Використати єдиний вхід" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "Ваша організація вимагає єдиний вхід (SSO)." }, "welcomeBack": { "message": "З поверненням" @@ -592,7 +592,10 @@ "message": "Переглянути" }, "viewAll": { - "message": "View all" + "message": "Переглянути все" + }, + "viewLess": { + "message": "View less" }, "viewLogin": { "message": "Переглянути запис" @@ -796,6 +799,12 @@ "onLocked": { "message": "З блокуванням системи" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "З перезапуском браузера" }, @@ -1035,10 +1044,10 @@ "message": "Запис збережено" }, "savedWebsite": { - "message": "Saved website" + "message": "Збережений вебсайт" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "Збережені вебсайти ( $COUNT$ )", "placeholders": { "count": { "content": "$1", @@ -1440,22 +1449,22 @@ "message": "Застаріле шифрування більше не підтримується. Зверніться до служби підтримки, щоб відновити обліковий запис." }, "premiumMembership": { - "message": "Преміум статус" + "message": "Передплата Premium" }, "premiumManage": { "message": "Керувати передплатою" }, "premiumManageAlert": { - "message": "Ви можете керувати своїм статусом у сховищі на bitwarden.com. Хочете перейти на вебсайт зараз?" + "message": "Ви можете керувати передплатою у сховищі на bitwarden.com. Хочете перейти на вебсайт зараз?" }, "premiumRefresh": { "message": "Оновити стан передплати" }, "premiumNotCurrentMember": { - "message": "Зараз у вас немає передплати преміум." + "message": "Зараз у вас немає передплати Premium." }, "premiumSignUpAndGet": { - "message": "Передплатіть преміум і отримайте:" + "message": "Передплатіть Premium і отримайте:" }, "ppremiumSignUpStorage": { "message": "1 ГБ зашифрованого сховища для файлів." @@ -1476,25 +1485,25 @@ "message": "Пріоритетну технічну підтримку." }, "ppremiumSignUpFuture": { - "message": "Усі майбутні преміумфункції. Їх буде більше!" + "message": "Усі майбутні функції Premium. Їх буде більше!" }, "premiumPurchase": { - "message": "Придбати преміум" + "message": "Придбати Premium" }, "premiumPurchaseAlertV2": { - "message": "Ви можете придбати Преміум у налаштуваннях облікового запису вебпрограмі Bitwarden." + "message": "Ви можете придбати Premium у налаштуваннях облікового запису вебпрограми Bitwarden." }, "premiumCurrentMember": { - "message": "Ви користуєтеся передплатою преміум!" + "message": "Ви користуєтеся передплатою Premium!" }, "premiumCurrentMemberThanks": { "message": "Дякуємо за підтримку Bitwarden." }, "premiumFeatures": { - "message": "Передплатіть преміум та отримайте:" + "message": "Передплатіть Premium та отримайте:" }, "premiumPrice": { - "message": "Всього лише $PRICE$ / за рік!", + "message": "Лише $PRICE$ / рік!", "placeholders": { "price": { "content": "$1", @@ -1503,7 +1512,7 @@ } }, "premiumPriceV2": { - "message": "Усе лише за $PRICE$ на рік!", + "message": "Лише за $PRICE$ на рік за все!", "placeholders": { "price": { "content": "$1", @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Запитувати біометрію під час запуску" }, - "premiumRequired": { - "message": "Необхідна передплата преміум" - }, - "premiumRequiredDesc": { - "message": "Для використання цієї функції необхідна передплата преміум." - }, "authenticationTimeout": { "message": "Час очікування автентифікації" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Необхідно додати URL-адресу основного сервера, або принаймні одне користувацьке середовище." }, + "selfHostedEnvMustUseHttps": { + "message": "URL-адреси повинні бути HTTPS." + }, "customEnvironment": { "message": "Власне середовище" }, @@ -1695,28 +1701,28 @@ "message": "Вимкніть автозаповнення" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "Підтвердити автозаповнення" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "Адреса цього вебсайту відрізняється від збережених даних вашого запису. Перш ніж заповнити облікові дані, переконайтеся, що це надійний сайт." }, "showInlineMenuLabel": { "message": "Пропозиції автозаповнення на полях форм" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Як Bitwarden захищає ваші дані від шахрайства?" }, "currentWebsite": { - "message": "Current website" + "message": "Поточний вебсайт" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "Автоматично заповнити й додати цей сайт" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "Автоматично заповнити без додавання" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "Не заповнювати автоматично" }, "showInlineMenuIdentitiesLabel": { "message": "Показувати посвідчення як пропозиції" @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Встановити головний пароль" }, @@ -3280,7 +3289,7 @@ "message": "Помилка розшифрування" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "Помилка отримання даних автозаповнення" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden не зміг розшифрувати вказані нижче елементи сховища." @@ -3512,7 +3521,7 @@ "message": "Помилка Key Connector: переконайтеся, що Key Connector доступний та працює правильно." }, "premiumSubcriptionRequired": { - "message": "Необхідна передплата преміум" + "message": "Необхідна передплата Premium" }, "organizationIsDisabled": { "message": "Організацію вимкнено." @@ -4054,13 +4063,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "Неможливо автоматично заповнити" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "Типово налаштовано \"Точну відповідність\". Адреса поточного вебсайту відрізняється від збережених даних для цього запису." }, "okay": { - "message": "Okay" + "message": "Гаразд" }, "toggleSideNavigation": { "message": "Перемкнути бічну навігацію" @@ -4891,7 +4900,7 @@ "message": "Ви дійсно хочете остаточно видалити це вкладення?" }, "premium": { - "message": "Преміум" + "message": "Premium" }, "freeOrgsCannotUseAttachments": { "message": "Організації без передплати не можуть використовувати вкладення" @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Показати виявлення збігів $WEBSITE$", "placeholders": { @@ -5769,14 +5788,56 @@ "atRiskLoginsSecured": { "message": "Ви чудово впоралися із захистом своїх ризикованих записів!" }, + "upgradeNow": { + "message": "Покращити" + }, + "builtInAuthenticator": { + "message": "Вбудований автентифікатор" + }, + "secureFileStorage": { + "message": "Захищене сховище файлів" + }, + "emergencyAccess": { + "message": "Екстрений доступ" + }, + "breachMonitoring": { + "message": "Моніторинг витоків даних" + }, + "andMoreFeatures": { + "message": "Інші можливості!" + }, + "planDescPremium": { + "message": "Повна онлайн-безпека" + }, + "upgradeToPremium": { + "message": "Покращити до Premium" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "Цей параметр вимкнено політикою вашої організації.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." }, "zipPostalCodeLabel": { - "message": "ZIP / Postal code" + "message": "Поштовий індекс" }, "cardNumberLabel": { - "message": "Card number" + "message": "Номер картки" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index fff32a542cc..242b779ca26 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -32,7 +32,7 @@ "message": "Dùng đăng nhập một lần" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "Tổ chức của bạn yêu cầu đăng nhập một lần." }, "welcomeBack": { "message": "Chào mừng bạn trở lại" @@ -592,7 +592,10 @@ "message": "Xem" }, "viewAll": { - "message": "View all" + "message": "Xem tất cả" + }, + "viewLess": { + "message": "View less" }, "viewLogin": { "message": "Xem đăng nhập" @@ -796,6 +799,12 @@ "onLocked": { "message": "Mỗi khi khóa máy" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Mỗi khi khởi động lại trình duyệt" }, @@ -1035,10 +1044,10 @@ "message": "Đã lưu mục" }, "savedWebsite": { - "message": "Saved website" + "message": "Đã lưu trang web" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "Đã lưu trang web ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "Yêu cầu sinh trắc học khi khởi chạy" }, - "premiumRequired": { - "message": "Cần có tài khoản Cao cấp" - }, - "premiumRequiredDesc": { - "message": "Cần là thành viên Cao cấp để sử dụng tính năng này." - }, "authenticationTimeout": { "message": "Thời gian chờ xác thực" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "Bạn phải thêm URL máy chủ cơ sở hoặc ít nhất một môi trường tùy chỉnh." }, + "selfHostedEnvMustUseHttps": { + "message": "URL phải sử dụng HTTPS." + }, "customEnvironment": { "message": "Môi trường tùy chỉnh" }, @@ -1695,28 +1701,28 @@ "message": "Tắt tự động điền" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "Xác nhận tự động điền" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "Trang web này không khớp với đăng nhập đã lưu của bạn. Trước khi bạn điền thông tin đăng nhập, hãy đảm bảo đây là trang web đáng tin cậy." }, "showInlineMenuLabel": { "message": "Hiển thị các gợi ý tự động điền trên các trường biểu mẫu" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Bitwarden bảo vệ dữ liệu của bạn khỏi lừa đảo như thế nào?" }, "currentWebsite": { - "message": "Current website" + "message": "Trang web hiện tại" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "Tự động điền và thêm trang web này" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "Tự động điền mà không thêm" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "Không tự động điền" }, "showInlineMenuIdentitiesLabel": { "message": "Hiển thị danh tính dưới dạng gợi ý" @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Đặt mật khẩu chính" }, @@ -3280,7 +3289,7 @@ "message": "Lỗi giải mã" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "Lỗi khi lấy dữ liệu tự động điền" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden không thể giải mã các mục trong kho lưu trữ được liệt kê bên dưới." @@ -4054,13 +4063,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "Không thể tự động điền" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "Phép so khớp mặc định được đặt thành \"Khớp chính xác\". Trang web hiện tại không khớp chính xác với đăng nhập đã lưu cho mục này." }, "okay": { - "message": "Okay" + "message": "Đồng ý" }, "toggleSideNavigation": { "message": "Ẩn/hiện thanh điều hướng bên" @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "Default ( $VALUE$ )", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "Hiện phát hiện trùng khớp $WEBSITE$", "placeholders": { @@ -5769,14 +5788,56 @@ "atRiskLoginsSecured": { "message": "Thật tuyệt khi bảo vệ các đăng nhập có nguy cơ của bạn!" }, + "upgradeNow": { + "message": "Nâng cấp ngay" + }, + "builtInAuthenticator": { + "message": "Trình xác thực tích hợp" + }, + "secureFileStorage": { + "message": "Lưu trữ tệp an toàn" + }, + "emergencyAccess": { + "message": "Truy cập khẩn cấp" + }, + "breachMonitoring": { + "message": "Giám sát vi phạm" + }, + "andMoreFeatures": { + "message": "Và nhiều hơn nữa!" + }, + "planDescPremium": { + "message": "Bảo mật trực tuyến toàn diện" + }, + "upgradeToPremium": { + "message": "Nâng cấp lên gói Cao cấp" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "Cài đặt này bị vô hiệu hóa bởi chính sách tổ chức của bạn.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." }, "zipPostalCodeLabel": { - "message": "ZIP / Postal code" + "message": "Mã ZIP / Bưu điện" }, "cardNumberLabel": { - "message": "Card number" + "message": "Số thẻ" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 16f41e4e987..cf1664b6a6f 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -32,7 +32,7 @@ "message": "使用单点登录" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "您的组织要求单点登录。" }, "welcomeBack": { "message": "欢迎回来" @@ -594,6 +594,9 @@ "viewAll": { "message": "查看全部" }, + "viewLess": { + "message": "查看更少" + }, "viewLogin": { "message": "查看登录" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "系统锁定时" }, + "onIdle": { + "message": "系统空闲时" + }, + "onSleep": { + "message": "系统睡眠时" + }, "onRestart": { "message": "浏览器重启时" }, @@ -1476,7 +1485,7 @@ "message": "优先客户支持。" }, "ppremiumSignUpFuture": { - "message": "未来的更多高级功能。敬请期待!" + "message": "未来的更多高级版功能。敬请期待!" }, "premiumPurchase": { "message": "购买高级版" @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "启动时提示生物识别" }, - "premiumRequired": { - "message": "需要高级会员" - }, - "premiumRequiredDesc": { - "message": "使用此功能需要高级会员资格。" - }, "authenticationTimeout": { "message": "身份验证超时" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "您必须添加基础服务器 URL 或至少添加一个自定义环境。" }, + "selfHostedEnvMustUseHttps": { + "message": "URL 必须使用 HTTPS。" + }, "customEnvironment": { "message": "自定义环境" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "此页面正在干扰 Bitwarden 的使用体验。出于安全考虑,Bitwarden 内嵌菜单已被暂时禁用。" + }, "setMasterPassword": { "message": "设置主密码" }, @@ -3729,7 +3738,7 @@ "message": "当前会话" }, "mobile": { - "message": "移动", + "message": "移动端", "description": "Mobile app" }, "extension": { @@ -4891,7 +4900,7 @@ "message": "确定要永久删除此附件吗?" }, "premium": { - "message": "高级会员" + "message": "高级版" }, "freeOrgsCannotUseAttachments": { "message": "免费组织无法使用附件" @@ -4968,7 +4977,17 @@ "message": "删除网站" }, "defaultLabel": { - "message": "默认 ($VALUE$)", + "message": "默认($VALUE$)", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, + "defaultLabelWithValue": { + "message": "默认($VALUE$)", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -5592,7 +5611,7 @@ "message": "欢迎使用 Bitwarden" }, "securityPrioritized": { - "message": "安全优先" + "message": "以安全为首要" }, "securityPrioritizedBody": { "message": "将登录、支付卡和身份保存到您的安全密码库。Bitwarden 使用零知识、端到端的加密来保护您的重要信息。" @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "很好地保护了存在风险的登录!" }, + "upgradeNow": { + "message": "立即升级" + }, + "builtInAuthenticator": { + "message": "内置身份验证器" + }, + "secureFileStorage": { + "message": "安全文件存储" + }, + "emergencyAccess": { + "message": "紧急访问" + }, + "breachMonitoring": { + "message": "数据泄露监测" + }, + "andMoreFeatures": { + "message": "以及更多!" + }, + "planDescPremium": { + "message": "全面的在线安全防护" + }, + "upgradeToPremium": { + "message": "升级为高级版" + }, + "upgradeCompleteSecurity": { + "message": "升级以获得全面的安全防护" + }, + "premiumGivesMoreTools": { + "message": "高级版为您提供更多工具,助您保障安全、高效工作并掌控一切。" + }, + "explorePremium": { + "message": "探索高级版" + }, + "loadingVault": { + "message": "正在加载密码库" + }, + "vaultLoaded": { + "message": "密码库已加载" + }, "settingDisabledByPolicy": { "message": "此设置被您组织的策略禁用了。", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "卡号" + }, + "sessionTimeoutSettingsAction": { + "message": "超时动作" } } diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index d3c0319e488..0ef20edce81 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -32,7 +32,7 @@ "message": "使用單一登入" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "您的組織需要單一登入。" }, "welcomeBack": { "message": "歡迎回來" @@ -594,6 +594,9 @@ "viewAll": { "message": "檢視全部" }, + "viewLess": { + "message": "顯示較少" + }, "viewLogin": { "message": "檢視登入" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "於系統鎖定時" }, + "onIdle": { + "message": "系統閒置時" + }, + "onSleep": { + "message": "系統睡眠時" + }, "onRestart": { "message": "於瀏覽器重新啟動時" }, @@ -1523,12 +1532,6 @@ "enableAutoBiometricsPrompt": { "message": "啟動時要求生物特徵辨識" }, - "premiumRequired": { - "message": "需要進階會員資格" - }, - "premiumRequiredDesc": { - "message": "進階會員才可使用此功能。" - }, "authenticationTimeout": { "message": "驗證逾時" }, @@ -1641,6 +1644,9 @@ "selfHostedEnvFormInvalid": { "message": "您必須新增伺服器網域 URL 或至少一個自訂環境。" }, + "selfHostedEnvMustUseHttps": { + "message": "URL 必須使用 HTTPS。" + }, "customEnvironment": { "message": "自訂環境" }, @@ -2430,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "此頁面正在干擾 Bitwarden 的使用體驗。為了安全起見,已暫時停用 Bitwarden 的內嵌選單。" + }, "setMasterPassword": { "message": "設定主密碼" }, @@ -4977,6 +4986,16 @@ } } }, + "defaultLabelWithValue": { + "message": "預設 ($VALUE$)", + "description": "A label that indicates the default value for a field with the current default value in parentheses.", + "placeholders": { + "value": { + "content": "$1", + "example": "Base domain" + } + } + }, "showMatchDetection": { "message": "顯示偵測到的吻合 $WEBSITE$", "placeholders": { @@ -5769,6 +5788,45 @@ "atRiskLoginsSecured": { "message": "你已成功保護有風險的登入項目,做得好!" }, + "upgradeNow": { + "message": "立即升級" + }, + "builtInAuthenticator": { + "message": "內建驗證器" + }, + "secureFileStorage": { + "message": "安全檔案儲存" + }, + "emergencyAccess": { + "message": "緊急存取" + }, + "breachMonitoring": { + "message": "外洩監控" + }, + "andMoreFeatures": { + "message": "以及其他功能功能!" + }, + "planDescPremium": { + "message": "完整的線上安全" + }, + "upgradeToPremium": { + "message": "升級到 Premium" + }, + "upgradeCompleteSecurity": { + "message": "升級以獲得完整的安全防護" + }, + "premiumGivesMoreTools": { + "message": "進階版提供更多工具,協助您維持安全、高效工作並保持掌控。" + }, + "explorePremium": { + "message": "探索進階版" + }, + "loadingVault": { + "message": "正在載入密碼庫" + }, + "vaultLoaded": { + "message": "已載入密碼庫" + }, "settingDisabledByPolicy": { "message": "此設定已被你的組織原則停用。", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5778,5 +5836,8 @@ }, "cardNumberLabel": { "message": "支付卡號碼" + }, + "sessionTimeoutSettingsAction": { + "message": "逾時後動作" } } diff --git a/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts b/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts index 9e9a1ecf570..d7d3c02ab14 100644 --- a/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts +++ b/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts @@ -122,10 +122,8 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy { async lock(userId: string) { this.loading = true; - await this.vaultTimeoutService.lock(userId); - // 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.router.navigate(["lock"]); + await this.lockService.lock(userId as UserId); + await this.router.navigate(["lock"]); } async lockAll() { diff --git a/apps/browser/src/auth/popup/account-switching/account.component.html b/apps/browser/src/auth/popup/account-switching/account.component.html index d22ce9c9366..90770bb8d9b 100644 --- a/apps/browser/src/auth/popup/account-switching/account.component.html +++ b/apps/browser/src/auth/popup/account-switching/account.component.html @@ -25,7 +25,7 @@
( - {{ + {{ status.text }} ) diff --git a/apps/browser/src/auth/popup/accounts/foreground-lock.service.ts b/apps/browser/src/auth/popup/accounts/foreground-lock.service.ts index 20a52a90d8b..91adecd4a03 100644 --- a/apps/browser/src/auth/popup/accounts/foreground-lock.service.ts +++ b/apps/browser/src/auth/popup/accounts/foreground-lock.service.ts @@ -6,10 +6,13 @@ import { MessageListener, MessageSender, } from "@bitwarden/common/platform/messaging"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { newGuid } from "@bitwarden/guid"; +import { UserId } from "@bitwarden/user-core"; const LOCK_ALL_FINISHED = new CommandDefinition<{ requestId: string }>("lockAllFinished"); const LOCK_ALL = new CommandDefinition<{ requestId: string }>("lockAll"); +const LOCK_USER_FINISHED = new CommandDefinition<{ requestId: string }>("lockUserFinished"); +const LOCK_USER = new CommandDefinition<{ requestId: string; userId: UserId }>("lockUser"); export class ForegroundLockService implements LockService { constructor( @@ -18,7 +21,7 @@ export class ForegroundLockService implements LockService { ) {} async lockAll(): Promise { - const requestId = Utils.newGuid(); + const requestId = newGuid(); const finishMessage = firstValueFrom( this.messageListener .messages$(LOCK_ALL_FINISHED) @@ -29,4 +32,19 @@ export class ForegroundLockService implements LockService { await finishMessage; } + + async lock(userId: UserId): Promise { + const requestId = newGuid(); + const finishMessage = firstValueFrom( + this.messageListener + .messages$(LOCK_USER_FINISHED) + .pipe(filter((m) => m.requestId === requestId)), + ); + + this.messageSender.send(LOCK_USER, { requestId, userId }); + + await finishMessage; + } + + async runPlatformOnLockActions(): Promise {} } diff --git a/apps/browser/src/auth/popup/components/set-pin.component.html b/apps/browser/src/auth/popup/components/set-pin.component.html index d525f9378f1..c88274b2bf4 100644 --- a/apps/browser/src/auth/popup/components/set-pin.component.html +++ b/apps/browser/src/auth/popup/components/set-pin.component.html @@ -1,6 +1,6 @@
-
+
{{ "setYourPinTitle" | i18n }}
diff --git a/apps/browser/src/auth/popup/settings/account-security.component.html b/apps/browser/src/auth/popup/settings/account-security.component.html index 44900acc065..37efcee9012 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.html +++ b/apps/browser/src/auth/popup/settings/account-security.component.html @@ -20,9 +20,9 @@ - {{ - "unlockWithBiometrics" | i18n - }} + + {{ "unlockWithBiometrics" | i18n }} + {{ biometricUnavailabilityReason }} @@ -38,9 +38,9 @@ type="checkbox" formControlName="enableAutoBiometricsPrompt" /> - {{ - "enableAutoBiometricsPrompt" | i18n - }} + + {{ "enableAutoBiometricsPrompt" | i18n }} + - {{ - "lockWithMasterPassOnRestart1" | i18n - }} + + {{ "lockWithMasterPassOnRestart1" | i18n }} + - -

{{ "vaultTimeoutHeader" | i18n }}

-
+ @if (consolidatedSessionTimeoutComponent$ | async) { + +

+ {{ "sessionTimeoutHeader" | i18n }} +

+
- - - + + + + } @else { + +

+ {{ "vaultTimeoutHeader" | i18n }} +

+
- - {{ "vaultTimeoutAction1" | i18n }} - - - - + + + - - {{ "unlockMethodNeededToChangeTimeoutActionDesc" | i18n }}
+ + {{ "vaultTimeoutAction1" | i18n }} + + + + + + + {{ "unlockMethodNeededToChangeTimeoutActionDesc" | i18n }}
+
+
+ + + {{ "vaultTimeoutPolicyAffectingOptions" | i18n }} -
- - - {{ "vaultTimeoutPolicyAffectingOptions" | i18n }} - -
+ + }
diff --git a/apps/browser/src/auth/popup/settings/account-security.component.spec.ts b/apps/browser/src/auth/popup/settings/account-security.component.spec.ts index aa3639e9e93..d0ab4793301 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.spec.ts +++ b/apps/browser/src/auth/popup/settings/account-security.component.spec.ts @@ -6,6 +6,7 @@ import { mock } from "jest-mock-extended"; import { firstValueFrom, of } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; +import { LockService } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -16,10 +17,10 @@ import { UserVerificationService } from "@bitwarden/common/auth/abstractions/use import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { VaultTimeoutSettingsService, - VaultTimeoutService, VaultTimeoutStringType, VaultTimeoutAction, } from "@bitwarden/common/key-management/vault-timeout"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -63,6 +64,8 @@ describe("AccountSecurityComponent", () => { const validationService = mock(); const dialogService = mock(); const platformUtilsService = mock(); + const lockService = mock(); + const configService = mock(); beforeEach(async () => { await TestBed.configureTestingModule({ @@ -83,7 +86,6 @@ describe("AccountSecurityComponent", () => { { provide: PopupRouterCacheService, useValue: mock() }, { provide: ToastService, useValue: mock() }, { provide: UserVerificationService, useValue: mock() }, - { provide: VaultTimeoutService, useValue: mock() }, { provide: VaultTimeoutSettingsService, useValue: vaultTimeoutSettingsService }, { provide: StateProvider, useValue: mock() }, { provide: CipherService, useValue: mock() }, @@ -92,6 +94,8 @@ describe("AccountSecurityComponent", () => { { provide: OrganizationService, useValue: mock() }, { provide: CollectionService, useValue: mock() }, { provide: ValidationService, useValue: validationService }, + { provide: LockService, useValue: lockService }, + { provide: ConfigService, useValue: configService }, ], }) .overrideComponent(AccountSecurityComponent, { diff --git a/apps/browser/src/auth/popup/settings/account-security.component.ts b/apps/browser/src/auth/popup/settings/account-security.component.ts index 65a0d33f93e..e6e7be96c08 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.ts +++ b/apps/browser/src/auth/popup/settings/account-security.component.ts @@ -24,22 +24,24 @@ import { import { JslibModule } from "@bitwarden/angular/jslib.module"; import { NudgesService, NudgeType } from "@bitwarden/angular/vault"; import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component"; -import { FingerprintDialogComponent, VaultTimeoutInputComponent } from "@bitwarden/auth/angular"; +import { FingerprintDialogComponent } from "@bitwarden/auth/angular"; +import { LockService } from "@bitwarden/auth/common"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { getFirstPolicy } from "@bitwarden/common/admin-console/services/policy/default-policy.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { VaultTimeout, VaultTimeoutAction, VaultTimeoutOption, - VaultTimeoutService, VaultTimeoutSettingsService, VaultTimeoutStringType, } from "@bitwarden/common/key-management/vault-timeout"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -67,6 +69,10 @@ import { BiometricStateService, BiometricsStatus, } from "@bitwarden/key-management"; +import { + SessionTimeoutInputComponent, + SessionTimeoutSettingsComponent, +} from "@bitwarden/key-management-ui"; import { BiometricErrors, BiometricErrorTypes } from "../../../models/biometricErrors"; import { BrowserApi } from "../../../platform/browser/browser-api"; @@ -100,9 +106,10 @@ import { AwaitDesktopDialogComponent } from "./await-desktop-dialog.component"; SectionComponent, SectionHeaderComponent, SelectModule, + SessionTimeoutSettingsComponent, SpotlightComponent, TypographyModule, - VaultTimeoutInputComponent, + SessionTimeoutInputComponent, ], }) export class AccountSecurityComponent implements OnInit, OnDestroy { @@ -133,17 +140,20 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { ), ); - private refreshTimeoutSettings$ = new BehaviorSubject(undefined); + protected readonly consolidatedSessionTimeoutComponent$: Observable; + + protected refreshTimeoutSettings$ = new BehaviorSubject(undefined); private destroy$ = new Subject(); constructor( private accountService: AccountService, + private configService: ConfigService, private pinService: PinServiceAbstraction, private policyService: PolicyService, private formBuilder: FormBuilder, private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, - private vaultTimeoutService: VaultTimeoutService, + private lockService: LockService, private vaultTimeoutSettingsService: VaultTimeoutSettingsService, public messagingService: MessagingService, private environmentService: EnvironmentService, @@ -157,7 +167,11 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { private vaultNudgesService: NudgesService, private validationService: ValidationService, private logService: LogService, - ) {} + ) { + this.consolidatedSessionTimeoutComponent$ = this.configService.getFeatureFlag$( + FeatureFlag.ConsolidatedSessionTimeoutComponent, + ); + } async ngOnInit() { const hasMasterPassword = await this.userVerificationService.hasMasterPassword(); @@ -173,6 +187,7 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { this.hasVaultTimeoutPolicy = true; } + // Determine platform-specific timeout options const showOnLocked = !this.platformUtilsService.isFirefox() && !this.platformUtilsService.isSafari() && @@ -695,7 +710,8 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { } async lock() { - await this.vaultTimeoutService.lock(); + const activeUserId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); + await this.lockService.lock(activeUserId); } async logOut() { diff --git a/apps/browser/src/auth/popup/settings/await-desktop-dialog.component.ts b/apps/browser/src/auth/popup/settings/await-desktop-dialog.component.ts index a64cea1ef3e..12cf669d89b 100644 --- a/apps/browser/src/auth/popup/settings/await-desktop-dialog.component.ts +++ b/apps/browser/src/auth/popup/settings/await-desktop-dialog.component.ts @@ -1,7 +1,12 @@ import { Component } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; +import { + ButtonModule, + CenterPositionStrategy, + DialogModule, + DialogService, +} from "@bitwarden/components"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @@ -11,6 +16,8 @@ import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components }) export class AwaitDesktopDialogComponent { static open(dialogService: DialogService) { - return dialogService.open(AwaitDesktopDialogComponent); + return dialogService.open(AwaitDesktopDialogComponent, { + positionStrategy: new CenterPositionStrategy(), + }); } } diff --git a/apps/browser/src/auth/services/extension-lock.service.ts b/apps/browser/src/auth/services/extension-lock.service.ts new file mode 100644 index 00000000000..7e01e8155e7 --- /dev/null +++ b/apps/browser/src/auth/services/extension-lock.service.ts @@ -0,0 +1,58 @@ +import { DefaultLockService, LogoutService } from "@bitwarden/auth/common"; +import MainBackground from "@bitwarden/browser/background/main.background"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; +import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { VaultTimeoutSettingsService } from "@bitwarden/common/key-management/vault-timeout"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { SystemService } from "@bitwarden/common/platform/abstractions/system.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; +import { BiometricsService, KeyService } from "@bitwarden/key-management"; +import { LogService } from "@bitwarden/logging"; +import { StateEventRunnerService } from "@bitwarden/state"; + +export class ExtensionLockService extends DefaultLockService { + constructor( + accountService: AccountService, + biometricService: BiometricsService, + vaultTimeoutSettingsService: VaultTimeoutSettingsService, + logoutService: LogoutService, + messagingService: MessagingService, + searchService: SearchService, + folderService: FolderService, + masterPasswordService: InternalMasterPasswordServiceAbstraction, + stateEventRunnerService: StateEventRunnerService, + cipherService: CipherService, + authService: AuthService, + systemService: SystemService, + processReloadService: ProcessReloadServiceAbstraction, + logService: LogService, + keyService: KeyService, + private readonly main: MainBackground, + ) { + super( + accountService, + biometricService, + vaultTimeoutSettingsService, + logoutService, + messagingService, + searchService, + folderService, + masterPasswordService, + stateEventRunnerService, + cipherService, + authService, + systemService, + processReloadService, + logService, + keyService, + ); + } + + async runPlatformOnLockActions(): Promise { + await this.main.refreshMenu(true); + } +} diff --git a/apps/browser/src/autofill/background/abstractions/notification.background.ts b/apps/browser/src/autofill/background/abstractions/notification.background.ts index 912d9657124..e50a317e8a7 100644 --- a/apps/browser/src/autofill/background/abstractions/notification.background.ts +++ b/apps/browser/src/autofill/background/abstractions/notification.background.ts @@ -147,7 +147,7 @@ type NotificationBackgroundExtensionMessageHandlers = { bgGetEnableChangedPasswordPrompt: () => Promise; bgGetEnableAddedLoginPrompt: () => Promise; bgGetExcludedDomains: () => Promise; - bgGetActiveUserServerConfig: () => Promise; + bgGetActiveUserServerConfig: () => Promise; getWebVaultUrlForNotification: () => Promise; }; diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index 6067d563db2..96809fa26b2 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -1,19 +1,22 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; +import { CipherIconDetails } from "@bitwarden/common/vault/icon/build-cipher-icon"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { InlineMenuFillType } from "../../enums/autofill-overlay.enum"; +import AutofillField from "../../models/autofill-field"; import AutofillPageDetails from "../../models/autofill-page-details"; import { PageDetail } from "../../services/abstractions/autofill.service"; import { LockedVaultPendingNotificationsData } from "./notification.background"; -export type PageDetailsForTab = Record< - chrome.runtime.MessageSender["tab"]["id"], - Map ->; +export type TabId = NonNullable; + +export type FrameId = NonNullable; + +type PageDetailsByFrame = Map; + +export type PageDetailsForTab = Record; export type SubFrameOffsetData = { top: number; @@ -21,19 +24,14 @@ export type SubFrameOffsetData = { url?: string; frameId?: number; parentFrameIds?: number[]; + isCrossOriginSubframe?: boolean; + isMainFrame?: boolean; + hasParentFrame?: boolean; } | null; -export type SubFrameOffsetsForTab = Record< - chrome.runtime.MessageSender["tab"]["id"], - Map ->; +type SubFrameOffsetsByFrame = Map; -export type WebsiteIconData = { - imageEnabled: boolean; - image: string; - fallbackImage: string; - icon: string; -}; +export type SubFrameOffsetsForTab = Record; export type UpdateOverlayCiphersParams = { updateAllCipherTypes: boolean; @@ -49,6 +47,7 @@ export type FocusedFieldData = { accountCreationFieldType?: string; showPasskeys?: boolean; focusedFieldForm?: string; + focusedFieldOpid?: string; }; export type InlineMenuElementPosition = { @@ -146,7 +145,7 @@ export type OverlayBackgroundExtensionMessage = { isFieldCurrentlyFilling?: boolean; subFrameData?: SubFrameOffsetData; focusedFieldData?: FocusedFieldData; - allFieldsRect?: any; + allFieldsRect?: AutofillField[]; isOpeningFullInlineMenu?: boolean; styles?: Partial; data?: LockedVaultPendingNotificationsData; @@ -155,13 +154,30 @@ export type OverlayBackgroundExtensionMessage = { ToggleInlineMenuHiddenMessage & UpdateInlineMenuVisibilityMessage; +export type OverlayPortCommand = + | "fillCipher" + | "addNewVaultItem" + | "viewCipher" + | "redirectFocus" + | "updateHeight" + | "buttonClicked" + | "blurred" + | "updateColorScheme" + | "unlockVault" + | "refreshGeneratedPassword" + | "fillGeneratedPassword"; + export type OverlayPortMessage = { - [key: string]: any; - command: string; - direction?: string; + command: OverlayPortCommand; + direction?: "up" | "down" | "left" | "right"; inlineMenuCipherId?: string; addNewCipherType?: CipherType; usePasskey?: boolean; + height?: number; + backgroundColorScheme?: "light" | "dark"; + viewsCipherData?: InlineMenuCipherData; + loginUrl?: string; + fillGeneratedPassword?: boolean; }; export type InlineMenuCipherData = { @@ -170,7 +186,7 @@ export type InlineMenuCipherData = { type: CipherType; reprompt: CipherRepromptType; favorite: boolean; - icon: WebsiteIconData; + icon: CipherIconDetails; accountCreationFieldType?: string; login?: { totp?: string; @@ -201,9 +217,14 @@ export type BuildCipherDataParams = { export type BackgroundMessageParam = { message: OverlayBackgroundExtensionMessage; }; + export type BackgroundSenderParam = { - sender: chrome.runtime.MessageSender; + sender: chrome.runtime.MessageSender & { + tab: NonNullable; + frameId: FrameId; + }; }; + export type BackgroundOnMessageHandlerParams = BackgroundMessageParam & BackgroundSenderParam; export type OverlayBackgroundExtensionMessageHandlers = { @@ -253,9 +274,13 @@ export type OverlayBackgroundExtensionMessageHandlers = { export type PortMessageParam = { message: OverlayPortMessage; }; + export type PortConnectionParam = { - port: chrome.runtime.Port; + port: chrome.runtime.Port & { + sender: NonNullable; + }; }; + export type PortOnMessageHandlerParams = PortMessageParam & PortConnectionParam; export type InlineMenuButtonPortMessageHandlers = { diff --git a/apps/browser/src/autofill/background/context-menus.background.ts b/apps/browser/src/autofill/background/context-menus.background.ts index 0db2fd59af3..8c99c0b065e 100644 --- a/apps/browser/src/autofill/background/context-menus.background.ts +++ b/apps/browser/src/autofill/background/context-menus.background.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { BrowserApi } from "../../platform/browser/browser-api"; import { ContextMenuClickedHandler } from "../browser/context-menu-clicked-handler"; @@ -17,9 +15,11 @@ export default class ContextMenusBackground { return; } - this.contextMenus.onClicked.addListener((info, tab) => - this.contextMenuClickedHandler.run(info, tab), - ); + this.contextMenus.onClicked.addListener((info, tab) => { + if (tab) { + return this.contextMenuClickedHandler.run(info, tab); + } + }); BrowserApi.messageListener( "contextmenus.background", @@ -28,18 +28,16 @@ export default class ContextMenusBackground { sender: chrome.runtime.MessageSender, ) => { if (msg.command === "unlockCompleted" && msg.data.target === "contextmenus.background") { - // 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.contextMenuClickedHandler - .cipherAction( - msg.data.commandToRetry.message.contextMenuOnClickData, - msg.data.commandToRetry.sender.tab, - ) - .then(() => { - // 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 - BrowserApi.tabSendMessageData(sender.tab, "closeNotificationBar"); + const onClickData = msg.data.commandToRetry.message.contextMenuOnClickData; + const senderTab = msg.data.commandToRetry.sender.tab; + + if (onClickData && senderTab) { + void this.contextMenuClickedHandler.cipherAction(onClickData, senderTab).then(() => { + if (sender.tab) { + void BrowserApi.tabSendMessageData(sender.tab, "closeNotificationBar"); + } }); + } } }, ); diff --git a/apps/browser/src/autofill/background/notification.background.spec.ts b/apps/browser/src/autofill/background/notification.background.spec.ts index f9e2e1c534f..8df21bc66ef 100644 --- a/apps/browser/src/autofill/background/notification.background.spec.ts +++ b/apps/browser/src/autofill/background/notification.background.spec.ts @@ -1530,5 +1530,63 @@ describe("NotificationBackground", () => { expect(environmentServiceSpy).toHaveBeenCalled(); }); }); + + describe("handleUnlockPopoutClosed", () => { + let onRemovedListeners: Array<(tabId: number, removeInfo: chrome.tabs.OnRemovedInfo) => void>; + let tabsQuerySpy: jest.SpyInstance; + + beforeEach(() => { + onRemovedListeners = []; + chrome.tabs.onRemoved.addListener = jest.fn((listener) => { + onRemovedListeners.push(listener); + }); + chrome.runtime.getURL = jest.fn().mockReturnValue("chrome-extension://id/popup/index.html"); + notificationBackground.init(); + }); + + const triggerTabRemoved = async (tabId: number) => { + onRemovedListeners[0](tabId, mock()); + await flushPromises(); + }; + + it("sends abandon message when unlock popout is closed and vault is locked", async () => { + activeAccountStatusMock$.next(AuthenticationStatus.Locked); + tabsQuerySpy = jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([]); + + await triggerTabRemoved(1); + + expect(tabsQuerySpy).toHaveBeenCalled(); + expect(messagingService.send).toHaveBeenCalledWith("abandonAutofillPendingNotifications"); + }); + + it("uses tracked tabId for fast lookup when available", async () => { + activeAccountStatusMock$.next(AuthenticationStatus.Locked); + tabsQuerySpy = jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([ + { + id: 123, + url: "chrome-extension://id/popup/index.html?singleActionPopout=auth_unlockExtension", + } as chrome.tabs.Tab, + ]); + + await triggerTabRemoved(999); + tabsQuerySpy.mockClear(); + messagingService.send.mockClear(); + + await triggerTabRemoved(123); + + expect(tabsQuerySpy).not.toHaveBeenCalled(); + expect(messagingService.send).toHaveBeenCalledWith("abandonAutofillPendingNotifications"); + }); + + it("returns early when vault is unlocked", async () => { + activeAccountStatusMock$.next(AuthenticationStatus.Unlocked); + tabsQuerySpy = jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([]); + + await triggerTabRemoved(1); + + expect(tabsQuerySpy).not.toHaveBeenCalled(); + expect(messagingService.send).not.toHaveBeenCalled(); + }); + }); }); }); diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index e27b50f13cd..547c5ba1575 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -45,7 +45,7 @@ import { SecurityTask } from "@bitwarden/common/vault/tasks/models/security-task // FIXME (PM-22628): Popup imports are forbidden in background // eslint-disable-next-line no-restricted-imports -import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window"; +import { AuthPopoutType, openUnlockPopout } from "../../auth/popup/utils/auth-popout-window"; import { BrowserApi } from "../../platform/browser/browser-api"; // FIXME (PM-22628): Popup imports are forbidden in background // eslint-disable-next-line no-restricted-imports @@ -89,6 +89,7 @@ export default class NotificationBackground { ExtensionCommand.AutofillCard, ExtensionCommand.AutofillIdentity, ]); + private unlockPopoutTabId?: number; private readonly extensionMessageHandlers: NotificationBackgroundExtensionMessageHandlers = { bgAdjustNotificationBar: ({ message, sender }) => this.handleAdjustNotificationBarMessage(message, sender), @@ -146,6 +147,7 @@ export default class NotificationBackground { } this.setupExtensionMessageListener(); + this.setupUnlockPopoutCloseListener(); this.cleanupNotificationQueue(); } @@ -1163,6 +1165,7 @@ export default class NotificationBackground { message: NotificationBackgroundExtensionMessage, sender: chrome.runtime.MessageSender, ): Promise { + this.unlockPopoutTabId = undefined; const messageData = message.data as LockedVaultPendingNotificationsData; const retryCommand = messageData.commandToRetry.message.command as ExtensionCommandType; if (this.allowedRetryCommands.has(retryCommand)) { @@ -1313,4 +1316,43 @@ export default class NotificationBackground { const tabDomain = Utils.getDomain(tab.url); return tabDomain === queueMessage.domain || tabDomain === Utils.getDomain(queueMessage.tab.url); } + + private setupUnlockPopoutCloseListener() { + chrome.tabs.onRemoved.addListener(async (tabId: number) => { + await this.handleUnlockPopoutClosed(tabId); + }); + } + + /** + * If the unlock popout is closed while the vault + * is still locked and there are pending autofill notifications, abandon them. + */ + private async handleUnlockPopoutClosed(removedTabId: number) { + const authStatus = await this.getAuthStatus(); + if (authStatus >= AuthenticationStatus.Unlocked) { + this.unlockPopoutTabId = undefined; + return; + } + + if (this.unlockPopoutTabId === removedTabId) { + this.unlockPopoutTabId = undefined; + this.messagingService.send("abandonAutofillPendingNotifications"); + return; + } + + if (this.unlockPopoutTabId) { + return; + } + + const extensionUrl = BrowserApi.getRuntimeURL("popup/index.html"); + const unlockPopoutTabs = (await BrowserApi.tabsQuery({ url: `${extensionUrl}*` })).filter( + (tab) => tab.url?.includes(`singleActionPopout=${AuthPopoutType.unlockExtension}`), + ); + + if (unlockPopoutTabs.length === 0) { + this.messagingService.send("abandonAutofillPendingNotifications"); + } else if (unlockPopoutTabs[0].id) { + this.unlockPopoutTabId = unlockPopoutTabs[0].id; + } + } } diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index 80e453e9e83..50fb291b121 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -3286,6 +3286,9 @@ describe("OverlayBackground", () => { pageDetails: [pageDetailsForTab], fillNewPassword: true, allowTotpAutofill: true, + focusedFieldForm: undefined, + focusedFieldOpid: undefined, + inlineMenuFillType: undefined, }); expect(overlayBackground["inlineMenuCiphers"].entries()).toStrictEqual( new Map([ @@ -3680,6 +3683,9 @@ describe("OverlayBackground", () => { pageDetails: [overlayBackground["pageDetailsForTab"][sender.tab.id].get(sender.frameId)], fillNewPassword: true, allowTotpAutofill: false, + focusedFieldForm: undefined, + focusedFieldOpid: undefined, + inlineMenuFillType: InlineMenuFillTypes.PasswordGeneration, }); }); }); diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 35585d58863..af8141f1ab8 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -1176,6 +1176,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { fillNewPassword: true, allowTotpAutofill: true, focusedFieldForm: this.focusedFieldData?.focusedFieldForm, + focusedFieldOpid: this.focusedFieldData?.focusedFieldOpid, + inlineMenuFillType: this.focusedFieldData?.inlineMenuFillType, }); if (totpCode) { @@ -1861,6 +1863,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { fillNewPassword: true, allowTotpAutofill: false, focusedFieldForm: this.focusedFieldData?.focusedFieldForm, + focusedFieldOpid: this.focusedFieldData?.focusedFieldOpid, + inlineMenuFillType: InlineMenuFillTypes.PasswordGeneration, }); globalThis.setTimeout(async () => { @@ -2945,17 +2949,21 @@ export class OverlayBackground implements OverlayBackgroundInterface { (await this.checkFocusedFieldHasValue(port.sender.tab)) && (await this.shouldShowSaveLoginInlineMenuList(port.sender.tab)); + const iframeUrl = BrowserApi.getRuntimeURL( + `overlay/menu-${isInlineMenuListPort ? "list" : "button"}.html`, + ); + const styleSheetUrl = BrowserApi.getRuntimeURL( + `overlay/menu-${isInlineMenuListPort ? "list" : "button"}.css`, + ); + const extensionOrigin = iframeUrl ? new URL(iframeUrl).origin : null; + this.postMessageToPort(port, { command: `initAutofillInlineMenu${isInlineMenuListPort ? "List" : "Button"}`, - iframeUrl: chrome.runtime.getURL( - `overlay/menu-${isInlineMenuListPort ? "list" : "button"}.html`, - ), + iframeUrl, pageTitle: chrome.i18n.getMessage( isInlineMenuListPort ? "bitwardenVault" : "bitwardenOverlayButton", ), - styleSheetUrl: chrome.runtime.getURL( - `overlay/menu-${isInlineMenuListPort ? "list" : "button"}.css`, - ), + styleSheetUrl, theme: await firstValueFrom(this.themeStateService.selectedTheme$), translations: this.getInlineMenuTranslations(), ciphers: isInlineMenuListPort ? await this.getInlineMenuCipherData() : null, @@ -2969,6 +2977,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { showSaveLoginMenu, showInlineMenuAccountCreation, authStatus, + extensionOrigin, }); this.updateInlineMenuPosition( port.sender, diff --git a/apps/browser/src/autofill/background/tabs.background.spec.ts b/apps/browser/src/autofill/background/tabs.background.spec.ts index 635ab8504a1..7bfa3b83c16 100644 --- a/apps/browser/src/autofill/background/tabs.background.spec.ts +++ b/apps/browser/src/autofill/background/tabs.background.spec.ts @@ -39,9 +39,7 @@ describe("TabsBackground", () => { "handleWindowOnFocusChanged", ); - // 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 - tabsBackground.init(); + void tabsBackground.init(); expect(chrome.windows.onFocusChanged.addListener).toHaveBeenCalledWith( handleWindowOnFocusChangedSpy, diff --git a/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts b/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts index c33cb6a4371..6f0979d4fd5 100644 --- a/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts +++ b/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts @@ -191,9 +191,11 @@ export class ContextMenuClickedHandler { }); } else { this.copyToClipboard({ text: cipher.login.password, tab: tab }); - // 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.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id); + + void this.eventCollectionService.collect( + EventType.Cipher_ClientCopiedPassword, + cipher.id, + ); } break; diff --git a/apps/browser/src/autofill/browser/main-context-menu-handler.ts b/apps/browser/src/autofill/browser/main-context-menu-handler.ts index 00ff55f5517..5a47975684c 100644 --- a/apps/browser/src/autofill/browser/main-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/main-context-menu-handler.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { firstValueFrom, map } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -179,9 +177,11 @@ export class MainContextMenuHandler { try { const account = await firstValueFrom(this.accountService.activeAccount$); - const hasPremium = await firstValueFrom( - this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), - ); + const hasPremium = + !!account?.id && + (await firstValueFrom( + this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), + )); const isCardRestricted = ( await firstValueFrom(this.restrictedItemTypesService.restricted$) @@ -198,14 +198,16 @@ export class MainContextMenuHandler { if (requiresPremiumAccess && !hasPremium) { continue; } - if (menuItem.id.startsWith(AUTOFILL_CARD_ID) && isCardRestricted) { + if (menuItem.id?.startsWith(AUTOFILL_CARD_ID) && isCardRestricted) { continue; } await MainContextMenuHandler.create({ ...otherOptions, contexts: ["all"] }); } } catch (error) { - this.logService.warning(error.message); + if (error instanceof Error) { + this.logService.warning(error.message); + } } finally { this.initRunning = false; } @@ -318,9 +320,11 @@ export class MainContextMenuHandler { } const account = await firstValueFrom(this.accountService.activeAccount$); - const canAccessPremium = await firstValueFrom( - this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), - ); + const canAccessPremium = + !!account?.id && + (await firstValueFrom( + this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), + )); if (canAccessPremium && (!cipher || !Utils.isNullOrEmpty(cipher.login?.totp))) { await createChildItem(COPY_VERIFICATION_CODE_ID); } @@ -333,7 +337,9 @@ export class MainContextMenuHandler { await createChildItem(AUTOFILL_IDENTITY_ID); } } catch (error) { - this.logService.warning(error.message); + if (error instanceof Error) { + this.logService.warning(error.message); + } } } @@ -351,7 +357,11 @@ export class MainContextMenuHandler { this.loadOptions( this.i18nService.t(authed ? "unlockVaultMenu" : "loginToVaultMenu"), NOOP_COMMAND_SUFFIX, - ).catch((error) => this.logService.warning(error.message)); + ).catch((error) => { + if (error instanceof Error) { + return this.logService.warning(error.message); + } + }); } } @@ -363,7 +373,9 @@ export class MainContextMenuHandler { } } } catch (error) { - this.logService.warning(error.message); + if (error instanceof Error) { + this.logService.warning(error.message); + } } } @@ -373,7 +385,9 @@ export class MainContextMenuHandler { await MainContextMenuHandler.create(menuItem); } } catch (error) { - this.logService.warning(error.message); + if (error instanceof Error) { + this.logService.warning(error.message); + } } } @@ -383,7 +397,9 @@ export class MainContextMenuHandler { await MainContextMenuHandler.create(menuItem); } } catch (error) { - this.logService.warning(error.message); + if (error instanceof Error) { + this.logService.warning(error.message); + } } } @@ -395,7 +411,9 @@ export class MainContextMenuHandler { await this.loadOptions(this.i18nService.t("addLoginMenu"), CREATE_LOGIN_ID); } catch (error) { - this.logService.warning(error.message); + if (error instanceof Error) { + this.logService.warning(error.message); + } } } } diff --git a/apps/browser/src/autofill/content/auto-submit-login.ts b/apps/browser/src/autofill/content/auto-submit-login.ts index ca5c8ebee80..511d35d7a49 100644 --- a/apps/browser/src/autofill/content/auto-submit-login.ts +++ b/apps/browser/src/autofill/content/auto-submit-login.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { EVENTS } from "@bitwarden/common/autofill/constants"; import AutofillPageDetails from "../models/autofill-page-details"; @@ -123,9 +121,9 @@ import { * @param fillScript - The autofill script to use */ function triggerAutoSubmitOnForm(fillScript: AutofillScript) { - const formOpid = fillScript.autosubmit[0]; + const formOpid = fillScript.autosubmit?.[0]; - if (formOpid === null) { + if (!formOpid) { triggerAutoSubmitOnFormlessFields(fillScript); return; } @@ -159,8 +157,11 @@ import { fillScript.script[fillScript.script.length - 1][1], ); - const lastFieldIsPasswordInput = - elementIsInputElement(currentElement) && currentElement.type === "password"; + const lastFieldIsPasswordInput = !!( + currentElement && + elementIsInputElement(currentElement) && + currentElement.type === "password" + ); while (currentElement && currentElement.tagName !== "HTML") { if (submitElementFoundAndClicked(currentElement, lastFieldIsPasswordInput)) { diff --git a/apps/browser/src/autofill/content/components/buttons/action-button.ts b/apps/browser/src/autofill/content/components/buttons/action-button.ts index b43bed7f96b..73fc1e79ec5 100644 --- a/apps/browser/src/autofill/content/components/buttons/action-button.ts +++ b/apps/browser/src/autofill/content/components/buttons/action-button.ts @@ -68,7 +68,7 @@ const actionButtonStyles = ({ overflow: hidden; text-align: center; text-overflow: ellipsis; - font-weight: 700; + font-weight: 500; ${disabled || isLoading ? ` diff --git a/apps/browser/src/autofill/content/components/cipher/types.ts b/apps/browser/src/autofill/content/components/cipher/types.ts index 590311682bf..f8b5d2b85bf 100644 --- a/apps/browser/src/autofill/content/components/cipher/types.ts +++ b/apps/browser/src/autofill/content/components/cipher/types.ts @@ -1,3 +1,5 @@ +import { CipherIconDetails } from "@bitwarden/common/vault/icon/build-cipher-icon"; + export const CipherTypes = { Login: 1, SecureNote: 2, @@ -22,20 +24,13 @@ export const OrganizationCategories = { family: "family", } as const; -export type WebsiteIconData = { - imageEnabled: boolean; - image: string; - fallbackImage: string; - icon: string; -}; - type BaseCipherData = { id: string; name: string; type: CipherTypeValue; reprompt: CipherRepromptType; favorite: boolean; - icon: WebsiteIconData; + icon: CipherIconDetails; }; export type CipherData = BaseCipherData & { diff --git a/apps/browser/src/autofill/content/components/notification/confirmation/message.ts b/apps/browser/src/autofill/content/components/notification/confirmation/message.ts index 01a2b783eda..36ea9c1f9d6 100644 --- a/apps/browser/src/autofill/content/components/notification/confirmation/message.ts +++ b/apps/browser/src/autofill/content/components/notification/confirmation/message.ts @@ -115,7 +115,7 @@ const notificationConfirmationButtonTextStyles = (theme: Theme) => css` ${baseTextStyles} color: ${themes[theme].primary[600]}; - font-weight: 700; + font-weight: 500; cursor: pointer; `; diff --git a/apps/browser/src/autofill/content/components/notification/header-message.ts b/apps/browser/src/autofill/content/components/notification/header-message.ts index 4b6e4722a83..2e51d82dd07 100644 --- a/apps/browser/src/autofill/content/components/notification/header-message.ts +++ b/apps/browser/src/autofill/content/components/notification/header-message.ts @@ -21,5 +21,5 @@ const notificationHeaderMessageStyles = (theme: Theme) => css` color: ${themes[theme].text.main}; font-family: Inter, sans-serif; font-size: 18px; - font-weight: 600; + font-weight: 500; `; diff --git a/apps/browser/src/autofill/content/components/option-selection/option-items.ts b/apps/browser/src/autofill/content/components/option-selection/option-items.ts index ceb72905357..58216b6c1b2 100644 --- a/apps/browser/src/autofill/content/components/option-selection/option-items.ts +++ b/apps/browser/src/autofill/content/components/option-selection/option-items.ts @@ -94,7 +94,7 @@ const optionsLabelStyles = ({ theme }: { theme: Theme }) => css` user-select: none; padding: 0.375rem ${spacing["3"]}; color: ${themes[theme].text.muted}; - font-weight: 600; + font-weight: 500; `; export const optionsMenuItemMaxWidth = 260; diff --git a/apps/browser/src/autofill/content/components/rows/action-row.ts b/apps/browser/src/autofill/content/components/rows/action-row.ts index 0380f91012a..8f13b166156 100644 --- a/apps/browser/src/autofill/content/components/rows/action-row.ts +++ b/apps/browser/src/autofill/content/components/rows/action-row.ts @@ -34,7 +34,7 @@ const actionRowStyles = (theme: Theme) => css` min-height: 40px; text-align: left; color: ${themes[theme].primary["600"]}; - font-weight: 700; + font-weight: 500; > span { display: block; diff --git a/apps/browser/src/autofill/content/content-message-handler.spec.ts b/apps/browser/src/autofill/content/content-message-handler.spec.ts index fe023f344d6..874e1cc76ff 100644 --- a/apps/browser/src/autofill/content/content-message-handler.spec.ts +++ b/apps/browser/src/autofill/content/content-message-handler.spec.ts @@ -56,7 +56,11 @@ describe("ContentMessageHandler", () => { }); it("sends an authResult message", () => { - postWindowMessage({ command: "authResult", lastpass: true, code: "code", state: "state" }); + postWindowMessage( + { command: "authResult", lastpass: true, code: "code", state: "state" }, + "https://localhost/", + window, + ); expect(sendMessageSpy).toHaveBeenCalledWith({ command: "authResult", @@ -68,7 +72,11 @@ describe("ContentMessageHandler", () => { }); it("sends a webAuthnResult message", () => { - postWindowMessage({ command: "webAuthnResult", data: "data", remember: true }); + postWindowMessage( + { command: "webAuthnResult", data: "data", remember: true }, + "https://localhost/", + window, + ); expect(sendMessageSpy).toHaveBeenCalledWith({ command: "webAuthnResult", @@ -82,7 +90,7 @@ describe("ContentMessageHandler", () => { const mockCode = "mockCode"; const command = "duoResult"; - postWindowMessage({ command: command, code: mockCode }); + postWindowMessage({ command: command, code: mockCode }, "https://localhost/", window); expect(sendMessageSpy).toHaveBeenCalledWith({ command: command, diff --git a/apps/browser/src/autofill/content/content-message-handler.ts b/apps/browser/src/autofill/content/content-message-handler.ts index c57b2d959f3..63afc215923 100644 --- a/apps/browser/src/autofill/content/content-message-handler.ts +++ b/apps/browser/src/autofill/content/content-message-handler.ts @@ -86,17 +86,30 @@ function handleOpenBrowserExtensionToUrlMessage({ url }: { url?: ExtensionPageUr } /** - * Handles the window message event. + * Handles window message events, validating source and extracting referrer for security. * * @param event - The window message event */ function handleWindowMessageEvent(event: MessageEvent) { - const { source, data } = event; + const { source, data, origin } = event; if (source !== window || !data?.command) { return; } - const referrer = source.location.hostname; + // Extract hostname from event.origin for secure referrer validation in background script + let referrer: string; + // Sandboxed iframe or opaque origin support + if (origin === "null") { + referrer = "null"; + } else { + try { + const originUrl = new URL(origin); + referrer = originUrl.hostname; + } catch { + return; + } + } + const handler = windowMessageHandlers[data.command]; if (handler) { handler({ data, referrer }); diff --git a/apps/browser/src/autofill/content/context-menu-handler.ts b/apps/browser/src/autofill/content/context-menu-handler.ts index 82cf95afc81..d3926d57c9a 100644 --- a/apps/browser/src/autofill/content/context-menu-handler.ts +++ b/apps/browser/src/autofill/content/context-menu-handler.ts @@ -1,43 +1,43 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore const inputTags = ["input", "textarea", "select"]; const labelTags = ["label", "span"]; -const attributes = ["id", "name", "label-aria", "placeholder"]; +const attributeKeys = ["id", "name", "label-aria", "placeholder"]; const invalidElement = chrome.i18n.getMessage("copyCustomFieldNameInvalidElement"); const noUniqueIdentifier = chrome.i18n.getMessage("copyCustomFieldNameNotUnique"); -let clickedEl: HTMLElement = null; +let clickedElement: HTMLElement | null = null; // Find the best attribute to be used as the Name for an element in a custom field. function getClickedElementIdentifier() { - if (clickedEl == null) { + if (clickedElement == null) { return invalidElement; } - const clickedTag = clickedEl.nodeName.toLowerCase(); - let inputEl = null; + const clickedTag = clickedElement.nodeName.toLowerCase(); + let inputElement = null; // Try to identify the input element (which may not be the clicked element) if (labelTags.includes(clickedTag)) { - let inputId = null; + let inputId; if (clickedTag === "label") { - inputId = clickedEl.getAttribute("for"); + inputId = clickedElement.getAttribute("for"); } else { - inputId = clickedEl.closest("label")?.getAttribute("for"); + inputId = clickedElement.closest("label")?.getAttribute("for"); } - inputEl = document.getElementById(inputId); + if (inputId) { + inputElement = document.getElementById(inputId); + } } else { - inputEl = clickedEl; + inputElement = clickedElement; } - if (inputEl == null || !inputTags.includes(inputEl.nodeName.toLowerCase())) { + if (inputElement == null || !inputTags.includes(inputElement.nodeName.toLowerCase())) { return invalidElement; } - for (const attr of attributes) { - const attributeValue = inputEl.getAttribute(attr); - const selector = "[" + attr + '="' + attributeValue + '"]'; + for (const attributeKey of attributeKeys) { + const attributeValue = inputElement.getAttribute(attributeKey); + const selector = "[" + attributeKey + '="' + attributeValue + '"]'; if (!isNullOrEmpty(attributeValue) && document.querySelectorAll(selector)?.length === 1) { return attributeValue; } @@ -45,14 +45,14 @@ function getClickedElementIdentifier() { return noUniqueIdentifier; } -function isNullOrEmpty(s: string) { +function isNullOrEmpty(s: string | null) { return s == null || s === ""; } // We only have access to the element that's been clicked when the context menu is first opened. // Remember it for use later. document.addEventListener("contextmenu", (event) => { - clickedEl = event.target as HTMLElement; + clickedElement = event.target as HTMLElement; }); // Runs when the 'Copy Custom Field Name' context menu item is actually clicked. @@ -62,9 +62,8 @@ chrome.runtime.onMessage.addListener((event, _sender, sendResponse) => { if (sendResponse) { sendResponse(identifier); } - // 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 - chrome.runtime.sendMessage({ + + void chrome.runtime.sendMessage({ command: "getClickedElementResponse", sender: "contextMenuHandler", identifier: identifier, diff --git a/apps/browser/src/autofill/fido2/background/abstractions/fido2.background.ts b/apps/browser/src/autofill/fido2/background/abstractions/fido2.background.ts index 6ad069ad56e..b341be28ebb 100644 --- a/apps/browser/src/autofill/fido2/background/abstractions/fido2.background.ts +++ b/apps/browser/src/autofill/fido2/background/abstractions/fido2.background.ts @@ -13,6 +13,7 @@ type SharedFido2ScriptRegistrationOptions = SharedFido2ScriptInjectionDetails & matches: string[]; excludeMatches: string[]; allFrames: true; + world?: "MAIN" | "ISOLATED"; }; type Fido2ExtensionMessage = { diff --git a/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts b/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts index 752851b3d37..adb59b8f845 100644 --- a/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts +++ b/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts @@ -203,6 +203,7 @@ describe("Fido2Background", () => { { file: Fido2ContentScript.PageScriptDelayAppend }, { file: Fido2ContentScript.ContentScript }, ], + world: "MAIN", ...sharedRegistrationOptions, }); }); diff --git a/apps/browser/src/autofill/fido2/background/fido2.background.ts b/apps/browser/src/autofill/fido2/background/fido2.background.ts index 22ee4a1822d..a8b016a14d6 100644 --- a/apps/browser/src/autofill/fido2/background/fido2.background.ts +++ b/apps/browser/src/autofill/fido2/background/fido2.background.ts @@ -176,6 +176,7 @@ export class Fido2Background implements Fido2BackgroundInterface { { file: await this.getFido2PageScriptAppendFileName() }, { file: Fido2ContentScript.ContentScript }, ], + world: "MAIN", ...this.sharedRegistrationOptions, }); } diff --git a/apps/browser/src/autofill/fido2/content/fido2-page-script-delay-append.mv2.ts b/apps/browser/src/autofill/fido2/content/fido2-page-script-delay-append.mv2.ts index 775bc76266d..e167f30af0a 100644 --- a/apps/browser/src/autofill/fido2/content/fido2-page-script-delay-append.mv2.ts +++ b/apps/browser/src/autofill/fido2/content/fido2-page-script-delay-append.mv2.ts @@ -8,6 +8,9 @@ } const script = globalContext.document.createElement("script"); + // This script runs in world: MAIN, eliminating the risk associated with this lint error. + // DOM injection is still needed for the iframe timing hack. + // eslint-disable-next-line @bitwarden/platform/no-page-script-url-leakage script.src = chrome.runtime.getURL("content/fido2-page-script.js"); script.async = false; diff --git a/apps/browser/src/autofill/fido2/content/fido2-page-script.ts b/apps/browser/src/autofill/fido2/content/fido2-page-script.ts index 5b9ea5e5b27..1cd614a9516 100644 --- a/apps/browser/src/autofill/fido2/content/fido2-page-script.ts +++ b/apps/browser/src/autofill/fido2/content/fido2-page-script.ts @@ -267,9 +267,7 @@ import { Messenger } from "./messaging/messenger"; clearWaitForFocus(); void messenger.destroy(); - // FIXME: Remove when updating file. Eslint update - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (e) { + } catch { /** empty */ } } diff --git a/apps/browser/src/autofill/fido2/content/messaging/messenger.spec.ts b/apps/browser/src/autofill/fido2/content/messaging/messenger.spec.ts index 5283c60882d..1aa8c27c0ae 100644 --- a/apps/browser/src/autofill/fido2/content/messaging/messenger.spec.ts +++ b/apps/browser/src/autofill/fido2/content/messaging/messenger.spec.ts @@ -31,9 +31,8 @@ describe("Messenger", () => { it("should deliver message to B when sending request from A", () => { const request = createRequest(); - // 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 - messengerA.request(request); + + void messengerA.request(request); const received = handlerB.receive(); @@ -66,14 +65,13 @@ describe("Messenger", () => { it("should deliver abort signal to B when requesting abort", () => { const abortController = new AbortController(); - // 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 - messengerA.request(createRequest(), abortController.signal); + + void messengerA.request(createRequest(), abortController.signal); abortController.abort(); const received = handlerB.receive(); - expect(received[0].abortController.signal.aborted).toBe(true); + expect(received[0].abortController?.signal.aborted).toBe(true); }); describe("destroy", () => { @@ -103,29 +101,25 @@ describe("Messenger", () => { it("should dispatch the destroy event on messenger destruction", async () => { const request = createRequest(); - // 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 - messengerA.request(request); + + void messengerA.request(request); const dispatchEventSpy = jest.spyOn((messengerA as any).onDestroy, "dispatchEvent"); - // 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 - messengerA.destroy(); + + void messengerA.destroy(); expect(dispatchEventSpy).toHaveBeenCalledWith(expect.any(Event)); }); it("should trigger onDestroyListener when the destroy event is dispatched", async () => { const request = createRequest(); - // 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 - messengerA.request(request); + + void messengerA.request(request); const onDestroyListener = jest.fn(); (messengerA as any).onDestroy.addEventListener("destroy", onDestroyListener); - // 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 - messengerA.destroy(); + + void messengerA.destroy(); expect(onDestroyListener).toHaveBeenCalled(); const eventArg = onDestroyListener.mock.calls[0][0]; @@ -213,7 +207,7 @@ class MockMessagePort { remotePort: MockMessagePort; postMessage(message: T, port?: MessagePort) { - this.remotePort.onmessage( + this.remotePort.onmessage?.( new MessageEvent("message", { data: message, ports: port ? [port] : [], diff --git a/apps/browser/src/autofill/fido2/services/browser-fido2-user-interface.service.ts b/apps/browser/src/autofill/fido2/services/browser-fido2-user-interface.service.ts index 8de48a49a8e..5818bbf8d82 100644 --- a/apps/browser/src/autofill/fido2/services/browser-fido2-user-interface.service.ts +++ b/apps/browser/src/autofill/fido2/services/browser-fido2-user-interface.service.ts @@ -6,7 +6,7 @@ import { filter, firstValueFrom, fromEvent, - fromEventPattern, + map, merge, Observable, Subject, @@ -28,6 +28,7 @@ import { import { Utils } from "@bitwarden/common/platform/misc/utils"; import { BrowserApi } from "../../../platform/browser/browser-api"; +import { fromChromeEvent } from "../../../platform/browser/from-chrome-event"; // FIXME (PM-22628): Popup imports are forbidden in background // eslint-disable-next-line no-restricted-imports import { closeFido2Popout, openFido2Popout } from "../../../vault/popup/utils/vault-popout-window"; @@ -154,9 +155,7 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi } static sendMessage(msg: BrowserFido2Message) { - // 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 - BrowserApi.sendMessage(BrowserFido2MessageName, msg); + void BrowserApi.sendMessage(BrowserFido2MessageName, msg); } static abortPopout(sessionId: string, fallbackRequested = false) { @@ -205,9 +204,7 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi fromEvent(abortController.signal, "abort") .pipe(takeUntil(this.destroy$)) .subscribe(() => { - // 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.close(); + void this.close(); BrowserFido2UserInterfaceSession.sendMessage({ type: BrowserFido2MessageTypes.AbortRequest, sessionId: this.sessionId, @@ -223,21 +220,13 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi ) .subscribe((msg) => { if (msg.type === BrowserFido2MessageTypes.AbortResponse) { - // 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.close(); - // 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.abort(msg.fallbackRequested); + void this.close(); + void this.abort(msg.fallbackRequested); } }); - this.windowClosed$ = fromEventPattern( - // FIXME: Make sure that is does not cause a memory leak in Safari or use BrowserApi.AddListener - // and test that it doesn't break. Tracking Ticket: https://bitwarden.atlassian.net/browse/PM-4735 - // eslint-disable-next-line no-restricted-syntax - (handler: any) => chrome.windows.onRemoved.addListener(handler), - (handler: any) => chrome.windows.onRemoved.removeListener(handler), + this.windowClosed$ = fromChromeEvent(chrome.windows.onRemoved).pipe( + map(([windowId]) => windowId), ); BrowserFido2UserInterfaceSession.sendMessage({ @@ -391,12 +380,8 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi takeUntil(this.destroy$), ) .subscribe(() => { - // 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.close(); - // 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.abort(true); + void this.close(); + void this.abort(true); }); await connectPromise; diff --git a/apps/browser/src/autofill/models/autofill-field.ts b/apps/browser/src/autofill/models/autofill-field.ts index 1a8c3bb875b..9d2cf3773d4 100644 --- a/apps/browser/src/autofill/models/autofill-field.ts +++ b/apps/browser/src/autofill/models/autofill-field.ts @@ -1,6 +1,4 @@ import { FieldRect } from "../background/abstractions/overlay.background"; -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { AutofillFieldQualifierType } from "../enums/autofill-field.enums"; import { InlineMenuAccountCreationFieldTypes, @@ -13,34 +11,36 @@ import { export default class AutofillField { [key: string]: any; /** - * The unique identifier assigned to this field during collection of the page details + * Non-null asserted. The unique identifier assigned to this field during collection of the page details */ - opid: string; + opid!: string; /** - * Sequential number assigned to each element collected, based on its position in the DOM. + * Non-null asserted. Sequential number assigned to each element collected, based on its position in the DOM. * Used to do perform proximal checks for username and password fields on the DOM. */ - elementNumber: number; + elementNumber!: number; /** - * Designates whether the field is viewable on the current part of the DOM that the user can see + * Non-null asserted. Designates whether the field is viewable on the current part of the DOM that the user can see */ - viewable: boolean; + viewable!: boolean; /** - * The HTML `id` attribute of the field + * Non-null asserted. The HTML `id` attribute of the field */ - htmlID: string | null; + htmlID!: string | null; /** - * The HTML `name` attribute of the field + * Non-null asserted. The HTML `name` attribute of the field */ - htmlName: string | null; + htmlName!: string | null; /** - * The HTML `class` attribute of the field + * Non-null asserted. The HTML `class` attribute of the field */ - htmlClass: string | null; + htmlClass!: string | null; - tabindex: string | null; + /** Non-null asserted. */ + tabindex!: string | null; - title: string | null; + /** Non-null asserted. */ + title!: string | null; /** * The `tagName` for the field */ diff --git a/apps/browser/src/autofill/models/autofill-form.ts b/apps/browser/src/autofill/models/autofill-form.ts index d335a81b3c4..e9161620527 100644 --- a/apps/browser/src/autofill/models/autofill-form.ts +++ b/apps/browser/src/autofill/models/autofill-form.ts @@ -1,28 +1,31 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore /** * Represents an HTML form whose elements can be autofilled */ export default class AutofillForm { [key: string]: any; + /** - * The unique identifier assigned to this field during collection of the page details + * Non-null asserted. The unique identifier assigned to this field during collection of the page details */ - opid: string; + opid!: string; + /** - * The HTML `name` attribute of the form field + * Non-null asserted. The HTML `name` attribute of the form field */ - htmlName: string; + htmlName!: string; + /** - * The HTML `id` attribute of the form field + * Non-null asserted. The HTML `id` attribute of the form field */ - htmlID: string; + htmlID!: string; + /** - * The HTML `action` attribute of the form field + * Non-null asserted. The HTML `action` attribute of the form field */ - htmlAction: string; + htmlAction!: string; + /** - * The HTML `method` attribute of the form field + * Non-null asserted. The HTML `method` attribute of the form field. */ - htmlMethod: string; + htmlMethod!: "get" | "post" | string; } diff --git a/apps/browser/src/autofill/models/autofill-page-details.ts b/apps/browser/src/autofill/models/autofill-page-details.ts index c32dfed4e43..ca8c66a3152 100644 --- a/apps/browser/src/autofill/models/autofill-page-details.ts +++ b/apps/browser/src/autofill/models/autofill-page-details.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import AutofillField from "./autofill-field"; import AutofillForm from "./autofill-form"; @@ -7,16 +5,20 @@ import AutofillForm from "./autofill-form"; * The details of a page that have been collected and can be used for autofill */ export default class AutofillPageDetails { - title: string; - url: string; - documentUrl: string; + /** Non-null asserted. */ + title!: string; + /** Non-null asserted. */ + url!: string; + /** Non-null asserted. */ + documentUrl!: string; /** - * A collection of all of the forms in the page DOM, keyed by their `opid` + * Non-null asserted. A collection of all of the forms in the page DOM, keyed by their `opid` */ - forms: { [id: string]: AutofillForm }; + forms!: { [id: string]: AutofillForm }; /** - * A collection of all the fields in the page DOM, keyed by their `opid` + * Non-null asserted. A collection of all the fields in the page DOM, keyed by their `opid` */ - fields: AutofillField[]; - collectedTimestamp: number; + fields!: AutofillField[]; + /** Non-null asserted. */ + collectedTimestamp!: number; } diff --git a/apps/browser/src/autofill/models/autofill-script.ts b/apps/browser/src/autofill/models/autofill-script.ts index 1da05e07308..43c85c58c9a 100644 --- a/apps/browser/src/autofill/models/autofill-script.ts +++ b/apps/browser/src/autofill/models/autofill-script.ts @@ -1,26 +1,33 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -// String values affect code flow in autofill.ts and must not be changed -export type FillScriptActions = "click_on_opid" | "focus_by_opid" | "fill_by_opid"; - export type FillScript = [action: FillScriptActions, opid: string, value?: string]; export type AutofillScriptProperties = { delay_between_operations?: number; }; +export const FillScriptActionTypes = { + fill_by_opid: "fill_by_opid", + click_on_opid: "click_on_opid", + focus_by_opid: "focus_by_opid", +} as const; + +// String values affect code flow in autofill.ts and must not be changed +export type FillScriptActions = keyof typeof FillScriptActionTypes; + export type AutofillInsertActions = { - fill_by_opid: ({ opid, value }: { opid: string; value: string }) => void; - click_on_opid: ({ opid }: { opid: string }) => void; - focus_by_opid: ({ opid }: { opid: string }) => void; + [FillScriptActionTypes.fill_by_opid]: ({ opid, value }: { opid: string; value: string }) => void; + [FillScriptActionTypes.click_on_opid]: ({ opid }: { opid: string }) => void; + [FillScriptActionTypes.focus_by_opid]: ({ opid }: { opid: string }) => void; }; export default class AutofillScript { script: FillScript[] = []; properties: AutofillScriptProperties = {}; - metadata: any = {}; // Unused, not written or read - autosubmit: string[]; // Appears to be unused, read but not written - savedUrls: string[]; - untrustedIframe: boolean; - itemType: string; // Appears to be unused, read but not written + /** Non-null asserted. */ + autosubmit!: string[] | null; // Appears to be unused, read but not written + /** Non-null asserted. */ + savedUrls!: string[]; + /** Non-null asserted. */ + untrustedIframe!: boolean; + /** Non-null asserted. */ + itemType!: string; // Appears to be unused, read but not written } diff --git a/apps/browser/src/autofill/notification/abstractions/notification-bar.ts b/apps/browser/src/autofill/notification/abstractions/notification-bar.ts index 7881d2f1cac..b23c3c17abb 100644 --- a/apps/browser/src/autofill/notification/abstractions/notification-bar.ts +++ b/apps/browser/src/autofill/notification/abstractions/notification-bar.ts @@ -51,6 +51,7 @@ type NotificationBarWindowMessage = { }; error?: string; initData?: NotificationBarIframeInitData; + parentOrigin?: string; }; type NotificationBarWindowMessageHandlers = { diff --git a/apps/browser/src/autofill/notification/bar.html b/apps/browser/src/autofill/notification/bar.html index c0b57de612e..8934fe6a031 100644 --- a/apps/browser/src/autofill/notification/bar.html +++ b/apps/browser/src/autofill/notification/bar.html @@ -1,5 +1,4 @@ - - + Bitwarden diff --git a/apps/browser/src/autofill/notification/bar.spec.ts b/apps/browser/src/autofill/notification/bar.spec.ts new file mode 100644 index 00000000000..ae60e2efc91 --- /dev/null +++ b/apps/browser/src/autofill/notification/bar.spec.ts @@ -0,0 +1,121 @@ +import { mock } from "jest-mock-extended"; + +import { postWindowMessage } from "../spec/testing-utils"; + +import { NotificationBarWindowMessage } from "./abstractions/notification-bar"; +import "./bar"; + +jest.mock("lit", () => ({ render: jest.fn() })); +jest.mock("@lit-labs/signals", () => ({ + signal: jest.fn((testValue) => ({ get: (): typeof testValue => testValue })), +})); +jest.mock("../content/components/notification/container", () => ({ + NotificationContainer: jest.fn(), +})); + +describe("NotificationBar iframe handleWindowMessage security", () => { + const trustedOrigin = "http://localhost"; + const maliciousOrigin = "https://malicious.com"; + + const createMessage = ( + overrides: Partial = {}, + ): NotificationBarWindowMessage => ({ + command: "initNotificationBar", + ...overrides, + }); + + beforeEach(() => { + Object.defineProperty(globalThis, "location", { + value: { search: `?parentOrigin=${encodeURIComponent(trustedOrigin)}` }, + writable: true, + configurable: true, + }); + Object.defineProperty(globalThis, "parent", { + value: mock(), + writable: true, + configurable: true, + }); + globalThis.dispatchEvent(new Event("load")); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it.each([ + { + description: "not from parent window", + message: () => createMessage(), + origin: trustedOrigin, + source: () => mock(), + }, + { + description: "with mismatched origin", + message: () => createMessage(), + origin: maliciousOrigin, + source: () => globalThis.parent, + }, + { + description: "without command field", + message: () => ({}), + origin: trustedOrigin, + source: () => globalThis.parent, + }, + { + description: "initNotificationBar with mismatched parentOrigin", + message: () => createMessage({ parentOrigin: maliciousOrigin }), + origin: trustedOrigin, + source: () => globalThis.parent, + }, + { + description: "when windowMessageOrigin is not set", + message: () => createMessage(), + origin: "different-origin", + source: () => globalThis.parent, + resetOrigin: true, + }, + { + description: "with null source", + message: () => createMessage(), + origin: trustedOrigin, + source: (): null => null, + }, + { + description: "with unknown command", + message: () => createMessage({ command: "unknownCommand" }), + origin: trustedOrigin, + source: () => globalThis.parent, + }, + ])("should reject messages $description", ({ message, origin, source, resetOrigin }) => { + if (resetOrigin) { + Object.defineProperty(globalThis, "location", { + value: { search: "" }, + writable: true, + configurable: true, + }); + } + const spy = jest.spyOn(globalThis.parent, "postMessage").mockImplementation(); + postWindowMessage(message(), origin, source()); + expect(spy).not.toHaveBeenCalled(); + }); + + it("should accept and handle valid trusted messages", () => { + const spy = jest.spyOn(globalThis.parent, "postMessage").mockImplementation(); + spy.mockClear(); + + const validMessage = createMessage({ + parentOrigin: trustedOrigin, + initData: { + type: "change", + isVaultLocked: false, + removeIndividualVault: false, + importType: null, + launchTimestamp: Date.now(), + }, + }); + postWindowMessage(validMessage, trustedOrigin, globalThis.parent); + expect(validMessage.command).toBe("initNotificationBar"); + expect(validMessage.parentOrigin).toBe(trustedOrigin); + expect(validMessage.initData).toBeDefined(); + }); +}); diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts index 3673a9f7321..333f8d5e534 100644 --- a/apps/browser/src/autofill/notification/bar.ts +++ b/apps/browser/src/autofill/notification/bar.ts @@ -24,6 +24,13 @@ import { let notificationBarIframeInitData: NotificationBarIframeInitData = {}; let windowMessageOrigin: string; +const urlParams = new URLSearchParams(globalThis.location.search); +const trustedParentOrigin = urlParams.get("parentOrigin"); + +if (trustedParentOrigin) { + windowMessageOrigin = trustedParentOrigin; +} + const notificationBarWindowMessageHandlers: NotificationBarWindowMessageHandlers = { initNotificationBar: ({ message }) => initNotificationBar(message), saveCipherAttemptCompleted: ({ message }) => handleSaveCipherConfirmation(message), @@ -395,15 +402,27 @@ function setupWindowMessageListener() { } function handleWindowMessage(event: MessageEvent) { - if (!windowMessageOrigin) { - windowMessageOrigin = event.origin; - } - - if (event.origin !== windowMessageOrigin) { + if (event?.source !== globalThis.parent) { return; } const message = event.data as NotificationBarWindowMessage; + if (!message?.command) { + return; + } + + if (!windowMessageOrigin || event.origin !== windowMessageOrigin) { + return; + } + + if ( + message.command === "initNotificationBar" && + message.parentOrigin && + message.parentOrigin !== event.origin + ) { + return; + } + const handler = notificationBarWindowMessageHandlers[message.command]; if (!handler) { return; @@ -431,5 +450,8 @@ function getResolvedTheme(theme: Theme) { } function postMessageToParent(message: NotificationBarWindowMessage) { - globalThis.parent.postMessage(message, windowMessageOrigin || "*"); + if (!windowMessageOrigin) { + return; + } + globalThis.parent.postMessage(message, windowMessageOrigin); } diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-button.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-button.ts index 642e7dd24e9..0836ecf5ff1 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-button.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-button.ts @@ -10,6 +10,7 @@ export type InitAutofillInlineMenuButtonMessage = UpdateAuthStatusMessage & { styleSheetUrl: string; translations: Record; portKey: string; + token: string; }; export type AutofillInlineMenuButtonWindowMessageHandlers = { diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-container.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-container.ts index a147e0ba165..98fd84373a8 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-container.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-container.ts @@ -5,6 +5,7 @@ import { InlineMenuCipherData } from "../../../background/abstractions/overlay.b export type AutofillInlineMenuContainerMessage = { command: string; portKey: string; + token: string; }; export type InitAutofillInlineMenuElementMessage = AutofillInlineMenuContainerMessage & { @@ -16,6 +17,7 @@ export type InitAutofillInlineMenuElementMessage = AutofillInlineMenuContainerMe translations: Record; ciphers: InlineMenuCipherData[] | null; portName: string; + extensionOrigin?: string; }; export type AutofillInlineMenuContainerWindowMessage = AutofillInlineMenuContainerMessage & diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts index f5e1fe08850..cf778ef7892 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts @@ -27,6 +27,7 @@ export type InitAutofillInlineMenuListMessage = AutofillInlineMenuListMessage & showInlineMenuAccountCreation?: boolean; showPasskeysLabels?: boolean; portKey: string; + token: string; generatedPassword?: string; showSaveLoginMenu?: boolean; }; diff --git a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.spec.ts index f1a74556b24..b7bd24c537b 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.spec.ts @@ -53,13 +53,35 @@ describe("AutofillInlineMenuContentService", () => { }); }); + describe("messageHandlers", () => { + it("returns the extension message handlers", () => { + const handlers = autofillInlineMenuContentService.messageHandlers; + + expect(handlers).toHaveProperty("closeAutofillInlineMenu"); + expect(handlers).toHaveProperty("appendAutofillInlineMenuToDom"); + }); + }); + describe("isElementInlineMenu", () => { - it("returns true if the passed element is the inline menu", () => { + it("returns true if the passed element is the inline menu list", () => { const element = document.createElement("div"); autofillInlineMenuContentService["listElement"] = element; expect(autofillInlineMenuContentService.isElementInlineMenu(element)).toBe(true); }); + + it("returns true if the passed element is the inline menu button", () => { + const element = document.createElement("div"); + autofillInlineMenuContentService["buttonElement"] = element; + + expect(autofillInlineMenuContentService.isElementInlineMenu(element)).toBe(true); + }); + + it("returns false if the passed element is not the inline menu", () => { + const element = document.createElement("div"); + + expect(autofillInlineMenuContentService.isElementInlineMenu(element)).toBe(false); + }); }); describe("extension message handlers", () => { @@ -388,7 +410,7 @@ describe("AutofillInlineMenuContentService", () => { }); it("closes the inline menu if the page body is not sufficiently opaque", async () => { - document.querySelector("html").style.opacity = "0.9"; + document.documentElement.style.opacity = "0.9"; document.body.style.opacity = "0"; await autofillInlineMenuContentService["handlePageMutations"]([mockBodyMutationRecord]); @@ -397,7 +419,7 @@ describe("AutofillInlineMenuContentService", () => { }); it("closes the inline menu if the page html is not sufficiently opaque", async () => { - document.querySelector("html").style.opacity = "0.3"; + document.documentElement.style.opacity = "0.3"; document.body.style.opacity = "0.7"; await autofillInlineMenuContentService["handlePageMutations"]([mockHTMLMutationRecord]); @@ -406,7 +428,7 @@ describe("AutofillInlineMenuContentService", () => { }); it("does not close the inline menu if the page html and body is sufficiently opaque", async () => { - document.querySelector("html").style.opacity = "0.9"; + document.documentElement.style.opacity = "0.9"; document.body.style.opacity = "1"; await autofillInlineMenuContentService["handlePageMutations"]([mockBodyMutationRecord]); await waitForIdleCallback(); @@ -599,5 +621,465 @@ describe("AutofillInlineMenuContentService", () => { overlayElement: AutofillOverlayElement.List, }); }); + + it("clears the persistent last child override timeout", () => { + jest.useFakeTimers(); + const clearTimeoutSpy = jest.spyOn(globalThis, "clearTimeout"); + autofillInlineMenuContentService["handlePersistentLastChildOverrideTimeout"] = setTimeout( + jest.fn(), + 500, + ); + + autofillInlineMenuContentService.destroy(); + + expect(clearTimeoutSpy).toHaveBeenCalled(); + }); + + it("unobserves page attributes", () => { + const disconnectSpy = jest.spyOn( + autofillInlineMenuContentService["htmlMutationObserver"], + "disconnect", + ); + + autofillInlineMenuContentService.destroy(); + + expect(disconnectSpy).toHaveBeenCalled(); + }); + }); + + describe("getOwnedTagNames", () => { + it("returns an empty array when no elements are created", () => { + expect(autofillInlineMenuContentService.getOwnedTagNames()).toEqual([]); + }); + + it("returns the button element tag name", () => { + const buttonElement = document.createElement("div"); + autofillInlineMenuContentService["buttonElement"] = buttonElement; + + const tagNames = autofillInlineMenuContentService.getOwnedTagNames(); + + expect(tagNames).toContain("DIV"); + }); + + it("returns both button and list element tag names", () => { + const buttonElement = document.createElement("div"); + const listElement = document.createElement("span"); + autofillInlineMenuContentService["buttonElement"] = buttonElement; + autofillInlineMenuContentService["listElement"] = listElement; + + const tagNames = autofillInlineMenuContentService.getOwnedTagNames(); + + expect(tagNames).toEqual(["DIV", "SPAN"]); + }); + }); + + describe("getUnownedTopLayerItems", () => { + beforeEach(() => { + document.body.innerHTML = ""; + }); + + it("returns the tag names from button and list elements", () => { + const buttonElement = document.createElement("div"); + buttonElement.setAttribute("popover", "manual"); + autofillInlineMenuContentService["buttonElement"] = buttonElement; + + const listElement = document.createElement("span"); + listElement.setAttribute("popover", "manual"); + autofillInlineMenuContentService["listElement"] = listElement; + + /** Mock querySelectorAll to avoid :modal selector issues in jsdom */ + const querySelectorAllSpy = jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([] as any); + + const items = autofillInlineMenuContentService.getUnownedTopLayerItems(); + + expect(querySelectorAllSpy).toHaveBeenCalled(); + expect(items.length).toBe(0); + }); + + it("calls querySelectorAll with correct selector when includeCandidates is false", () => { + /** Mock querySelectorAll to avoid :modal selector issues in jsdom */ + const querySelectorAllSpy = jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([] as any); + + autofillInlineMenuContentService.getUnownedTopLayerItems(false); + + const calledSelector = querySelectorAllSpy.mock.calls[0][0]; + expect(calledSelector).toContain(":modal"); + expect(calledSelector).toContain(":popover-open"); + }); + + it("includes candidates selector when requested", () => { + /** Mock querySelectorAll to avoid :modal selector issues in jsdom */ + const querySelectorAllSpy = jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([] as any); + + autofillInlineMenuContentService.getUnownedTopLayerItems(true); + + const calledSelector = querySelectorAllSpy.mock.calls[0][0]; + expect(calledSelector).toContain("[popover], dialog"); + }); + }); + + describe("refreshTopLayerPosition", () => { + it("does nothing when inline menu is disabled", () => { + const getUnownedTopLayerItemsSpy = jest.spyOn( + autofillInlineMenuContentService, + "getUnownedTopLayerItems", + ); + + autofillInlineMenuContentService["inlineMenuEnabled"] = false; + const buttonElement = document.createElement("div"); + autofillInlineMenuContentService["buttonElement"] = buttonElement; + + autofillInlineMenuContentService.refreshTopLayerPosition(); + + // Should exit early and not call `getUnownedTopLayerItems` + expect(getUnownedTopLayerItemsSpy).not.toHaveBeenCalled(); + }); + + it("does nothing when no other top layer items exist", () => { + const buttonElement = document.createElement("div"); + autofillInlineMenuContentService["buttonElement"] = buttonElement; + jest + .spyOn(autofillInlineMenuContentService, "getUnownedTopLayerItems") + .mockReturnValue([] as any); + + const getElementsByTagSpy = jest.spyOn(globalThis.document, "getElementsByTagName"); + + autofillInlineMenuContentService.refreshTopLayerPosition(); + + // Should exit early and not get inline elements to refresh + expect(getElementsByTagSpy).not.toHaveBeenCalled(); + }); + + it("refreshes button popover when button is in document", () => { + jest + .spyOn(autofillInlineMenuContentService, "getUnownedTopLayerItems") + .mockReturnValue([document.createElement("div")] as any); + + const buttonElement = document.createElement("div"); + buttonElement.setAttribute("popover", "manual"); + buttonElement.showPopover = jest.fn(); + buttonElement.hidePopover = jest.fn(); + document.body.appendChild(buttonElement); + autofillInlineMenuContentService["buttonElement"] = buttonElement; + + autofillInlineMenuContentService.refreshTopLayerPosition(); + + expect(buttonElement.hidePopover).toHaveBeenCalled(); + expect(buttonElement.showPopover).toHaveBeenCalled(); + }); + + it("refreshes list popover when list is in document", () => { + jest + .spyOn(autofillInlineMenuContentService, "getUnownedTopLayerItems") + .mockReturnValue([document.createElement("div")] as any); + + const listElement = document.createElement("div"); + listElement.setAttribute("popover", "manual"); + listElement.showPopover = jest.fn(); + listElement.hidePopover = jest.fn(); + document.body.appendChild(listElement); + autofillInlineMenuContentService["listElement"] = listElement; + + autofillInlineMenuContentService.refreshTopLayerPosition(); + + expect(listElement.hidePopover).toHaveBeenCalled(); + expect(listElement.showPopover).toHaveBeenCalled(); + }); + }); + + describe("checkAndUpdateRefreshCount", () => { + beforeEach(() => { + jest.useFakeTimers(); + jest.setSystemTime(new Date("2023-01-01T00:00:00.000Z")); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it("does nothing when inline menu is disabled", () => { + autofillInlineMenuContentService["inlineMenuEnabled"] = false; + + autofillInlineMenuContentService["checkAndUpdateRefreshCount"]("topLayer"); + + expect(autofillInlineMenuContentService["refreshCountWithinTimeThreshold"].topLayer).toBe(0); + }); + + it("increments refresh count when within time threshold", () => { + autofillInlineMenuContentService["lastTrackedTimestamp"].topLayer = Date.now() - 1000; + + autofillInlineMenuContentService["checkAndUpdateRefreshCount"]("topLayer"); + + expect(autofillInlineMenuContentService["refreshCountWithinTimeThreshold"].topLayer).toBe(1); + }); + + it("resets count when outside time threshold", () => { + autofillInlineMenuContentService["lastTrackedTimestamp"].topLayer = Date.now() - 6000; + autofillInlineMenuContentService["refreshCountWithinTimeThreshold"].topLayer = 5; + + autofillInlineMenuContentService["checkAndUpdateRefreshCount"]("topLayer"); + + expect(autofillInlineMenuContentService["refreshCountWithinTimeThreshold"].topLayer).toBe(0); + }); + + it("disables inline menu and shows alert when count exceeds threshold", () => { + const alertSpy = jest.spyOn(globalThis.window, "alert").mockImplementation(); + const checkPageRisksSpy = jest.spyOn( + autofillInlineMenuContentService as any, + "checkPageRisks", + ); + autofillInlineMenuContentService["lastTrackedTimestamp"].topLayer = Date.now() - 1000; + autofillInlineMenuContentService["refreshCountWithinTimeThreshold"].topLayer = 6; + + autofillInlineMenuContentService["checkAndUpdateRefreshCount"]("topLayer"); + + expect(autofillInlineMenuContentService["inlineMenuEnabled"]).toBe(false); + expect(alertSpy).toHaveBeenCalled(); + expect(checkPageRisksSpy).toHaveBeenCalled(); + }); + }); + + describe("refreshPopoverAttribute", () => { + it("calls checkAndUpdateRefreshCount with popoverAttribute type", () => { + const checkSpy = jest.spyOn( + autofillInlineMenuContentService as any, + "checkAndUpdateRefreshCount", + ); + const element = document.createElement("div"); + element.setAttribute("popover", "auto"); + element.showPopover = jest.fn(); + + autofillInlineMenuContentService["refreshPopoverAttribute"](element); + + expect(checkSpy).toHaveBeenCalledWith("popoverAttribute"); + expect(element.getAttribute("popover")).toBe("manual"); + expect(element.showPopover).toHaveBeenCalled(); + }); + }); + + describe("handleInlineMenuElementMutationObserverUpdate - popover attribute", () => { + it("refreshes popover attribute when changed from manual", () => { + const element = document.createElement("div"); + element.setAttribute("popover", "auto"); + element.showPopover = jest.fn(); + const refreshSpy = jest.spyOn( + autofillInlineMenuContentService as any, + "refreshPopoverAttribute", + ); + autofillInlineMenuContentService["buttonElement"] = element; + + const mockMutation = createMutationRecordMock({ + target: element, + type: "attributes", + attributeName: "popover", + }); + + autofillInlineMenuContentService["handleInlineMenuElementMutationObserverUpdate"]([ + mockMutation, + ]); + + expect(refreshSpy).toHaveBeenCalledWith(element); + }); + + it("does not refresh popover attribute when already manual", () => { + const element = document.createElement("div"); + element.setAttribute("popover", "manual"); + const refreshSpy = jest.spyOn( + autofillInlineMenuContentService as any, + "refreshPopoverAttribute", + ); + autofillInlineMenuContentService["buttonElement"] = element; + + const mockMutation = createMutationRecordMock({ + target: element, + type: "attributes", + attributeName: "popover", + }); + + autofillInlineMenuContentService["handleInlineMenuElementMutationObserverUpdate"]([ + mockMutation, + ]); + + expect(refreshSpy).not.toHaveBeenCalled(); + }); + }); + + describe("appendInlineMenuElements when disabled", () => { + beforeEach(() => { + observeContainerMutationsSpy.mockImplementation(); + }); + + it("does not append button when inline menu is disabled", async () => { + autofillInlineMenuContentService["inlineMenuEnabled"] = false; + jest.spyOn(globalThis.document.body, "appendChild"); + + sendMockExtensionMessage({ + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.Button, + }); + await flushPromises(); + + expect(globalThis.document.body.appendChild).not.toHaveBeenCalled(); + }); + + it("does not append list when inline menu is disabled", async () => { + autofillInlineMenuContentService["inlineMenuEnabled"] = false; + jest.spyOn(globalThis.document.body, "appendChild"); + + sendMockExtensionMessage({ + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.List, + }); + await flushPromises(); + + expect(globalThis.document.body.appendChild).not.toHaveBeenCalled(); + }); + }); + + describe("custom element creation for non-Firefox browsers", () => { + beforeEach(() => { + autofillInlineMenuContentService["isFirefoxBrowser"] = false; + observeContainerMutationsSpy.mockImplementation(); + }); + + it("creates a custom element for button in non-Firefox browsers", () => { + const definespy = jest.spyOn(globalThis.customElements, "define"); + + sendMockExtensionMessage({ + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.Button, + }); + + expect(definespy).toHaveBeenCalled(); + expect(autofillInlineMenuContentService["buttonElement"]).toBeDefined(); + expect(autofillInlineMenuContentService["buttonElement"]?.tagName).not.toBe("DIV"); + }); + + it("creates a custom element for list in non-Firefox browsers", () => { + const defineSpy = jest.spyOn(globalThis.customElements, "define"); + + sendMockExtensionMessage({ + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.List, + }); + + expect(defineSpy).toHaveBeenCalled(); + expect(autofillInlineMenuContentService["listElement"]).toBeDefined(); + expect(autofillInlineMenuContentService["listElement"]?.tagName).not.toBe("DIV"); + }); + }); + + describe("getPageIsOpaque", () => { + it("returns false when no page elements exist", () => { + jest.spyOn(globalThis.document, "querySelectorAll").mockReturnValue([] as any); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(false); + }); + + it("returns true when all html and body nodes have sufficient opacity", () => { + jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([document.documentElement, document.body] as any); + jest + .spyOn(globalThis.window, "getComputedStyle") + .mockImplementation(() => ({ opacity: "1" }) as CSSStyleDeclaration); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(true); + }); + + it("returns false when html opacity is below threshold", () => { + jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([document.documentElement, document.body] as any); + let callCount = 0; + jest.spyOn(globalThis.window, "getComputedStyle").mockImplementation(() => { + callCount++; + return { opacity: callCount === 1 ? "0.5" : "1" } as CSSStyleDeclaration; + }); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(false); + }); + + it("returns false when body opacity is below threshold", () => { + jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([document.documentElement, document.body] as any); + let callCount = 0; + jest.spyOn(globalThis.window, "getComputedStyle").mockImplementation(() => { + callCount++; + return { opacity: callCount === 1 ? "1" : "0.5" } as CSSStyleDeclaration; + }); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(false); + }); + + it("returns false when opacity of at least one duplicate body is below threshold", () => { + const duplicateBody = document.createElement("body"); + jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([document.documentElement, document.body, duplicateBody] as any); + let callCount = 0; + jest.spyOn(globalThis.window, "getComputedStyle").mockImplementation(() => { + callCount++; + + let opacityValue = "0.5"; + switch (callCount) { + case 1: + opacityValue = "1"; + break; + case 2: + opacityValue = "0.7"; + break; + default: + break; + } + + return { opacity: opacityValue } as CSSStyleDeclaration; + }); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(false); + }); + + it("returns true when opacity is above threshold", () => { + jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([document.documentElement, document.body] as any); + jest + .spyOn(globalThis.window, "getComputedStyle") + .mockImplementation(() => ({ opacity: "0.7" }) as CSSStyleDeclaration); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(true); + }); + + it("returns false when opacity is at threshold", () => { + jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([document.documentElement, document.body] as any); + jest + .spyOn(globalThis.window, "getComputedStyle") + .mockImplementation(() => ({ opacity: "0.6" }) as CSSStyleDeclaration); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(false); + }); }); }); diff --git a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts index b550ae203d5..b61e5e19d53 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts @@ -22,6 +22,19 @@ import { import { AutofillInlineMenuButtonIframe } from "../iframe-content/autofill-inline-menu-button-iframe"; import { AutofillInlineMenuListIframe } from "../iframe-content/autofill-inline-menu-list-iframe"; +const experienceValidationBackoffThresholds = { + topLayer: { + countLimit: 5, + timeSpanLimit: 5000, + }, + popoverAttribute: { + countLimit: 10, + timeSpanLimit: 5000, + }, +}; + +type BackoffCheckType = keyof typeof experienceValidationBackoffThresholds; + export class AutofillInlineMenuContentService implements AutofillInlineMenuContentServiceInterface { private readonly sendExtensionMessage = sendExtensionMessage; private readonly generateRandomCustomElementName = generateRandomCustomElementName; @@ -35,6 +48,19 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte private bodyMutationObserver: MutationObserver; private inlineMenuElementsMutationObserver: MutationObserver; private containerElementMutationObserver: MutationObserver; + private refreshCountWithinTimeThreshold: { [key in BackoffCheckType]: number } = { + topLayer: 0, + popoverAttribute: 0, + }; + private lastTrackedTimestamp = { + topLayer: Date.now(), + popoverAttribute: Date.now(), + }; + /** + * Distinct from preventing inline menu script injection, this is for cases + * where the page is subsequently determined to be risky. + */ + private inlineMenuEnabled = true; private mutationObserverIterations = 0; private mutationObserverIterationsResetTimeout: number | NodeJS.Timeout; private handlePersistentLastChildOverrideTimeout: number | NodeJS.Timeout; @@ -140,6 +166,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * Updates the position of both the inline menu button and inline menu list. */ private async appendInlineMenuElements({ overlayElement }: AutofillExtensionMessage) { + if (!this.inlineMenuEnabled) { + return; + } + if (overlayElement === AutofillOverlayElement.Button) { return this.appendButtonElement(); } @@ -151,6 +181,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * Updates the position of the inline menu button. */ private async appendButtonElement(): Promise { + if (!this.inlineMenuEnabled) { + return; + } + if (!this.buttonElement) { this.createButtonElement(); this.updateCustomElementDefaultStyles(this.buttonElement); @@ -167,6 +201,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * Updates the position of the inline menu list. */ private async appendListElement(): Promise { + if (!this.inlineMenuEnabled) { + return; + } + if (!this.listElement) { this.createListElement(); this.updateCustomElementDefaultStyles(this.listElement); @@ -219,6 +257,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * to create the element if it already exists in the DOM. */ private createButtonElement() { + if (!this.inlineMenuEnabled) { + return; + } + if (this.isFirefoxBrowser) { this.buttonElement = globalThis.document.createElement("div"); this.buttonElement.setAttribute("popover", "manual"); @@ -240,8 +282,6 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte this.buttonElement = globalThis.document.createElement(customElementName); this.buttonElement.setAttribute("popover", "manual"); - - this.createInternalStyleNode(this.buttonElement); } /** @@ -249,6 +289,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * to create the element if it already exists in the DOM. */ private createListElement() { + if (!this.inlineMenuEnabled) { + return; + } + if (this.isFirefoxBrowser) { this.listElement = globalThis.document.createElement("div"); this.listElement.setAttribute("popover", "manual"); @@ -270,30 +314,6 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte this.listElement = globalThis.document.createElement(customElementName); this.listElement.setAttribute("popover", "manual"); - - this.createInternalStyleNode(this.listElement); - } - - /** - * Builds and prepends an internal stylesheet to the container node with rules - * to prevent targeting by the host's global styling rules. This should only be - * used for pseudo elements such as `::backdrop` or `::before`. All other - * styles should be applied inline upon the parent container itself. - */ - private createInternalStyleNode(parent: HTMLElement) { - const css = document.createTextNode(` - ${parent.tagName}::backdrop { - background: none !important; - pointer-events: none !important; - } - ${parent.tagName}::before, ${parent.tagName}::after { - content:"" !important; - } - `); - const style = globalThis.document.createElement("style"); - style.setAttribute("type", "text/css"); - style.appendChild(css); - parent.prepend(style); } /** @@ -407,14 +427,23 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte } const element = record.target as HTMLElement; - if (record.attributeName !== "style") { - this.removeModifiedElementAttributes(element); + if (record.attributeName === "popover" && this.inlineMenuEnabled) { + const attributeValue = element.getAttribute(record.attributeName); + if (attributeValue !== "manual") { + this.refreshPopoverAttribute(element); + } continue; } - element.removeAttribute("style"); - this.updateCustomElementDefaultStyles(element); + if (record.attributeName === "style") { + element.removeAttribute("style"); + this.updateCustomElementDefaultStyles(element); + + continue; + } + + this.removeModifiedElementAttributes(element); } }; @@ -428,7 +457,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte const attributes = Array.from(element.attributes); for (let attributeIndex = 0; attributeIndex < attributes.length; attributeIndex++) { const attribute = attributes[attributeIndex]; - if (attribute.name === "style") { + if (attribute.name === "style" || attribute.name === "popover") { continue; } @@ -458,7 +487,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte private checkPageRisks = async () => { const pageIsOpaque = await this.getPageIsOpaque(); - const risksFound = !pageIsOpaque; + const risksFound = !pageIsOpaque || !this.inlineMenuEnabled; if (risksFound) { this.closeInlineMenu(); @@ -509,7 +538,49 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte return otherTopLayeritems; }; + /** + * Internally track owned injected experience refreshes as a side-effect + * of host page interference. + */ + private checkAndUpdateRefreshCount = (countType: BackoffCheckType) => { + if (!this.inlineMenuEnabled) { + return; + } + + const { countLimit, timeSpanLimit } = experienceValidationBackoffThresholds[countType]; + const now = Date.now(); + const timeSinceLastTrackedRefresh = now - this.lastTrackedTimestamp[countType]; + const currentlyWithinTimeThreshold = timeSinceLastTrackedRefresh <= timeSpanLimit; + const withinCountThreshold = this.refreshCountWithinTimeThreshold[countType] <= countLimit; + + if (currentlyWithinTimeThreshold) { + if (withinCountThreshold) { + this.refreshCountWithinTimeThreshold[countType]++; + } else { + // Set inline menu to be off; page is aggressively trying to take top position of top layer + this.inlineMenuEnabled = false; + void this.checkPageRisks(); + + const warningMessage = chrome.i18n.getMessage("topLayerHijackWarning"); + globalThis.window.alert(warningMessage); + } + } else { + this.lastTrackedTimestamp[countType] = now; + this.refreshCountWithinTimeThreshold[countType] = 0; + } + }; + + private refreshPopoverAttribute = (element: HTMLElement) => { + this.checkAndUpdateRefreshCount("popoverAttribute"); + element.setAttribute("popover", "manual"); + element.showPopover(); + }; + refreshTopLayerPosition = () => { + if (!this.inlineMenuEnabled) { + return; + } + const otherTopLayerItems = this.getUnownedTopLayerItems(); // No need to refresh if there are no other top-layer items @@ -523,6 +594,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte const listInDocument = this.listElement && (globalThis.document.getElementsByTagName(this.listElement.tagName)[0] as HTMLElement); + if (buttonInDocument) { buttonInDocument.hidePopover(); buttonInDocument.showPopover(); @@ -532,6 +604,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte listInDocument.hidePopover(); listInDocument.showPopover(); } + + if (buttonInDocument || listInDocument) { + this.checkAndUpdateRefreshCount("topLayer"); + } }; /** @@ -541,24 +617,28 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * `body` (enforced elsewhere). */ private getPageIsOpaque = () => { - // These are computed style values, so we don't need to worry about non-float values - // for `opacity`, here // @TODO for definitive checks, traverse up the node tree from the inline menu container; // nodes can exist between `html` and `body` - const htmlElement = globalThis.document.querySelector("html"); - const bodyElement = globalThis.document.querySelector("body"); + /** + * `querySelectorAll` for (non-standard) cases where the page has additional copies of + * page nodes that should be unique + */ + const pageElements = globalThis.document.querySelectorAll("html, body"); - if (!htmlElement || !bodyElement) { + if (!pageElements.length) { return false; } - const htmlOpacity = globalThis.window.getComputedStyle(htmlElement)?.opacity || "0"; - const bodyOpacity = globalThis.window.getComputedStyle(bodyElement)?.opacity || "0"; + return [...pageElements].every((element) => { + // These are computed style values, so we don't need to worry about non-float values + // for `opacity`, here + const elementOpacity = globalThis.window.getComputedStyle(element)?.opacity || "0"; - // Any value above this is considered "opaque" for our purposes - const opacityThreshold = 0.6; + // Any value above this is considered "opaque" for our purposes + const opacityThreshold = 0.6; - return parseFloat(htmlOpacity) > opacityThreshold && parseFloat(bodyOpacity) > opacityThreshold; + return parseFloat(elementOpacity) > opacityThreshold; + }); }; /** diff --git a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe-element.ts b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe-element.ts index 2fea65a7f01..3e2b364b17b 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe-element.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe-element.ts @@ -8,7 +8,10 @@ export class AutofillInlineMenuIframeElement { iframeTitle: string, ariaAlert?: string, ) { + const style = this.createInternalStyleNode(); const shadow: ShadowRoot = element.attachShadow({ mode: "closed" }); + shadow.prepend(style); + const autofillInlineMenuIframeService = new AutofillInlineMenuIframeService( shadow, portName, @@ -18,4 +21,50 @@ export class AutofillInlineMenuIframeElement { ); autofillInlineMenuIframeService.initMenuIframe(); } + + /** + * Builds and prepends an internal stylesheet to the container node with rules + * to prevent targeting by the host's global styling rules. This should only be + * used for pseudo elements such as `::backdrop` or `::before`. All other + * styles should be applied inline upon the parent container itself for improved + * specificity priority. + */ + private createInternalStyleNode() { + const css = document.createTextNode(` + :host::backdrop, + :host::before, + :host::after { + all: initial !important; + backdrop-filter: none !important; + filter: none !important; + inset: auto !important; + touch-action: auto !important; + user-select: text !important; + display: none !important; + position: relative !important; + top: auto !important; + right: auto !important; + bottom: auto !important; + left: auto !important; + transform: none !important; + transform-origin: 50% 50% !important; + opacity: 1 !important; + mix-blend-mode: normal !important; + isolation: isolate !important; + z-index: 0 !important; + background: none !important; + background-color: transparent !important; + background-image: none !important; + width: 0 !important; + height: 0 !important; + content: "" !important; + pointer-events: all !important; + } + `); + const style = globalThis.document.createElement("style"); + style.setAttribute("type", "text/css"); + style.appendChild(css); + + return style; + } } diff --git a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.spec.ts index 9f2947c2e99..3bb86ee7876 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.spec.ts @@ -191,7 +191,7 @@ describe("AutofillInlineMenuIframeService", () => { expect( autofillInlineMenuIframeService["iframe"].contentWindow.postMessage, - ).toHaveBeenCalledWith(message, "*"); + ).toHaveBeenCalledWith(message, autofillInlineMenuIframeService["extensionOrigin"]); }); it("handles port messages that are registered with the message handlers and does not pass the message on to the iframe", () => { @@ -217,7 +217,7 @@ describe("AutofillInlineMenuIframeService", () => { expect(autofillInlineMenuIframeService["portKey"]).toBe(portKey); expect( autofillInlineMenuIframeService["iframe"].contentWindow.postMessage, - ).toHaveBeenCalledWith(message, "*"); + ).toHaveBeenCalledWith(message, autofillInlineMenuIframeService["extensionOrigin"]); }); }); @@ -242,7 +242,7 @@ describe("AutofillInlineMenuIframeService", () => { expect(updateElementStylesSpy).not.toHaveBeenCalled(); expect( autofillInlineMenuIframeService["iframe"].contentWindow.postMessage, - ).toHaveBeenCalledWith(message, "*"); + ).toHaveBeenCalledWith(message, autofillInlineMenuIframeService["extensionOrigin"]); }); it("sets a light theme based on the user's system preferences", () => { @@ -262,7 +262,7 @@ describe("AutofillInlineMenuIframeService", () => { command: "initAutofillInlineMenuList", theme: ThemeType.Light, }, - "*", + autofillInlineMenuIframeService["extensionOrigin"], ); }); @@ -283,7 +283,7 @@ describe("AutofillInlineMenuIframeService", () => { command: "initAutofillInlineMenuList", theme: ThemeType.Dark, }, - "*", + autofillInlineMenuIframeService["extensionOrigin"], ); }); @@ -387,7 +387,7 @@ describe("AutofillInlineMenuIframeService", () => { command: "updateAutofillInlineMenuColorScheme", colorScheme: "normal", }, - "*", + autofillInlineMenuIframeService["extensionOrigin"], ); }); diff --git a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts index 9a9821f643c..8b1423b1290 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts @@ -3,6 +3,7 @@ import { EVENTS } from "@bitwarden/common/autofill/constants"; import { ThemeTypes } from "@bitwarden/common/platform/enums"; +import { BrowserApi } from "../../../../platform/browser/browser-api"; import { sendExtensionMessage, setElementStyles } from "../../../utils"; import { BackgroundPortMessageHandlers, @@ -15,6 +16,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe private readonly sendExtensionMessage = sendExtensionMessage; private port: chrome.runtime.Port | null = null; private portKey: string; + private readonly extensionOrigin: string; private iframeMutationObserver: MutationObserver; private iframe: HTMLIFrameElement; private ariaAlertElement: HTMLDivElement; @@ -69,6 +71,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe private iframeTitle: string, private ariaAlert?: string, ) { + this.extensionOrigin = BrowserApi.getRuntimeURL("")?.slice(0, -1); this.iframeMutationObserver = new MutationObserver(this.handleMutations); } @@ -81,7 +84,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe * that is declared. */ initMenuIframe() { - this.defaultIframeAttributes.src = chrome.runtime.getURL("overlay/menu.html"); + this.defaultIframeAttributes.src = BrowserApi.getRuntimeURL("overlay/menu.html"); this.defaultIframeAttributes.title = this.iframeTitle; this.iframe = globalThis.document.createElement("iframe"); @@ -259,7 +262,10 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe } private postMessageToIFrame(message: any) { - this.iframe.contentWindow?.postMessage({ portKey: this.portKey, ...message }, "*"); + this.iframe.contentWindow?.postMessage( + { portKey: this.portKey, ...message }, + this.extensionOrigin, + ); } /** diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.spec.ts index 7fa07850f00..10f6c905342 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.spec.ts @@ -1,5 +1,6 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { BrowserApi } from "../../../../../platform/browser/browser-api"; import { createInitAutofillInlineMenuButtonMessageMock } from "../../../../spec/autofill-mocks"; import { flushPromises, postWindowMessage } from "../../../../spec/testing-utils"; @@ -10,11 +11,11 @@ describe("AutofillInlineMenuButton", () => { let autofillInlineMenuButton: AutofillInlineMenuButton; const portKey: string = "inlineMenuButtonPortKey"; + const expectedOrigin = BrowserApi.getRuntimeURL("")?.slice(0, -1) || "chrome-extension://id"; beforeEach(() => { document.body.innerHTML = ``; autofillInlineMenuButton = document.querySelector("autofill-inline-menu-button"); - autofillInlineMenuButton["messageOrigin"] = "https://localhost/"; jest.spyOn(globalThis.document, "createElement"); jest.spyOn(globalThis.parent, "postMessage"); }); @@ -56,8 +57,8 @@ describe("AutofillInlineMenuButton", () => { autofillInlineMenuButton["buttonElement"].click(); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "autofillInlineMenuButtonClicked", portKey }, - "*", + { command: "autofillInlineMenuButtonClicked", portKey, token: "test-token" }, + expectedOrigin, ); }); }); @@ -70,7 +71,7 @@ describe("AutofillInlineMenuButton", () => { it("does not post a message to close the autofill inline menu if the element is focused during the focus check", async () => { jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(true); - postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused" }); + postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused", token: "test-token" }); await flushPromises(); expect(globalThis.parent.postMessage).not.toHaveBeenCalledWith({ @@ -84,7 +85,7 @@ describe("AutofillInlineMenuButton", () => { .spyOn(autofillInlineMenuButton["buttonElement"], "querySelector") .mockReturnValue(autofillInlineMenuButton["buttonElement"]); - postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused" }); + postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused", token: "test-token" }); await flushPromises(); expect(globalThis.parent.postMessage).not.toHaveBeenCalledWith({ @@ -98,7 +99,7 @@ describe("AutofillInlineMenuButton", () => { jest .spyOn(autofillInlineMenuButton["buttonElement"], "querySelector") .mockReturnValue(autofillInlineMenuButton["buttonElement"]); - postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused" }); + postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused", token: "test-token" }); await flushPromises(); globalThis.document.dispatchEvent(new MouseEvent("mouseout")); @@ -113,12 +114,12 @@ describe("AutofillInlineMenuButton", () => { jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(false); jest.spyOn(autofillInlineMenuButton["buttonElement"], "querySelector").mockReturnValue(null); - postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused" }); + postWindowMessage({ command: "checkAutofillInlineMenuButtonFocused", token: "test-token" }); await flushPromises(); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "triggerDelayedAutofillInlineMenuClosure", portKey }, - "*", + { command: "triggerDelayedAutofillInlineMenuClosure", portKey, token: "test-token" }, + expectedOrigin, ); }); @@ -128,6 +129,7 @@ describe("AutofillInlineMenuButton", () => { postWindowMessage({ command: "updateAutofillInlineMenuButtonAuthStatus", authStatus: AuthenticationStatus.Unlocked, + token: "test-token", }); await flushPromises(); @@ -143,6 +145,7 @@ describe("AutofillInlineMenuButton", () => { postWindowMessage({ command: "updateAutofillInlineMenuColorScheme", colorScheme: "dark", + token: "test-token", }); await flushPromises(); diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.ts index 4f497172b39..414673a9b81 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import "@webcomponents/custom-elements"; import "lit/polyfill-support.js"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; @@ -103,7 +101,10 @@ export class AutofillInlineMenuButton extends AutofillInlineMenuPageElement { */ private updatePageColorScheme({ colorScheme }: AutofillInlineMenuButtonMessage) { const colorSchemeMetaTag = globalThis.document.querySelector("meta[name='color-scheme']"); - colorSchemeMetaTag?.setAttribute("content", colorScheme); + + if (colorSchemeMetaTag && colorScheme) { + colorSchemeMetaTag.setAttribute("content", colorScheme); + } } /** diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts index b4e480797da..81bf7240230 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts @@ -3,6 +3,7 @@ import { mock } from "jest-mock-extended"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { CipherType } from "@bitwarden/common/vault/enums"; +import { BrowserApi } from "../../../../../platform/browser/browser-api"; import { InlineMenuCipherData } from "../../../../background/abstractions/overlay.background"; import { createAutofillOverlayCipherDataMock, @@ -23,6 +24,7 @@ describe("AutofillInlineMenuList", () => { let autofillInlineMenuList: AutofillInlineMenuList | null; const portKey: string = "inlineMenuListPortKey"; + const expectedOrigin = BrowserApi.getRuntimeURL("")?.slice(0, -1) || "chrome-extension://id"; const events: { eventName: any; callback: any }[] = []; beforeEach(() => { @@ -67,8 +69,8 @@ describe("AutofillInlineMenuList", () => { unlockButton.dispatchEvent(new Event("click")); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "unlockVault", portKey }, - "*", + { command: "unlockVault", portKey, token: "test-token" }, + expectedOrigin, ); }); }); @@ -134,8 +136,13 @@ describe("AutofillInlineMenuList", () => { addVaultItemButton.dispatchEvent(new Event("click")); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "addNewVaultItem", portKey, addNewCipherType: CipherType.Login }, - "*", + { + command: "addNewVaultItem", + portKey, + addNewCipherType: CipherType.Login, + token: "test-token", + }, + expectedOrigin, ); }); }); @@ -324,8 +331,9 @@ describe("AutofillInlineMenuList", () => { inlineMenuCipherId: "1", usePasskey: false, portKey, + token: "test-token", }, - "*", + expectedOrigin, ); }); @@ -492,8 +500,13 @@ describe("AutofillInlineMenuList", () => { viewCipherButton.dispatchEvent(new Event("click")); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "viewSelectedCipher", inlineMenuCipherId: "1", portKey }, - "*", + { + command: "viewSelectedCipher", + inlineMenuCipherId: "1", + portKey, + token: "test-token", + }, + expectedOrigin, ); }); @@ -581,8 +594,13 @@ describe("AutofillInlineMenuList", () => { newVaultItemButtonSpy.dispatchEvent(new Event("click")); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "addNewVaultItem", portKey, addNewCipherType: CipherType.Login }, - "*", + { + command: "addNewVaultItem", + portKey, + addNewCipherType: CipherType.Login, + token: "test-token", + }, + expectedOrigin, ); }); @@ -826,8 +844,8 @@ describe("AutofillInlineMenuList", () => { fillGeneratedPasswordButton.dispatchEvent(new Event("click")); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "fillGeneratedPassword", portKey }, - "*", + { command: "fillGeneratedPassword", portKey, token: "test-token" }, + expectedOrigin, ); }); @@ -843,7 +861,7 @@ describe("AutofillInlineMenuList", () => { expect(globalThis.parent.postMessage).not.toHaveBeenCalledWith( { command: "fillGeneratedPassword", portKey }, - "*", + expectedOrigin, ); }); @@ -857,8 +875,8 @@ describe("AutofillInlineMenuList", () => { ); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "fillGeneratedPassword", portKey }, - "*", + { command: "fillGeneratedPassword", portKey, token: "test-token" }, + expectedOrigin, ); }); @@ -896,8 +914,8 @@ describe("AutofillInlineMenuList", () => { refreshGeneratedPasswordButton.dispatchEvent(new Event("click")); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "refreshGeneratedPassword", portKey }, - "*", + { command: "refreshGeneratedPassword", portKey, token: "test-token" }, + expectedOrigin, ); }); @@ -913,7 +931,7 @@ describe("AutofillInlineMenuList", () => { expect(globalThis.parent.postMessage).not.toHaveBeenCalledWith( { command: "refreshGeneratedPassword", portKey }, - "*", + expectedOrigin, ); }); @@ -927,8 +945,8 @@ describe("AutofillInlineMenuList", () => { ); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "refreshGeneratedPassword", portKey }, - "*", + { command: "refreshGeneratedPassword", portKey, token: "test-token" }, + expectedOrigin, ); }); @@ -972,7 +990,7 @@ describe("AutofillInlineMenuList", () => { it("does not post a `checkAutofillInlineMenuButtonFocused` message to the parent if the inline menu is currently focused", () => { jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(true); - postWindowMessage({ command: "checkAutofillInlineMenuListFocused" }); + postWindowMessage({ command: "checkAutofillInlineMenuListFocused", token: "test-token" }); expect(globalThis.parent.postMessage).not.toHaveBeenCalled(); }); @@ -983,7 +1001,7 @@ describe("AutofillInlineMenuList", () => { .spyOn(autofillInlineMenuList["inlineMenuListContainer"], "querySelector") .mockReturnValue(autofillInlineMenuList["inlineMenuListContainer"]); - postWindowMessage({ command: "checkAutofillInlineMenuListFocused" }); + postWindowMessage({ command: "checkAutofillInlineMenuListFocused", token: "test-token" }); expect(globalThis.parent.postMessage).not.toHaveBeenCalled(); }); @@ -994,7 +1012,7 @@ describe("AutofillInlineMenuList", () => { jest .spyOn(autofillInlineMenuList["inlineMenuListContainer"], "querySelector") .mockReturnValue(autofillInlineMenuList["inlineMenuListContainer"]); - postWindowMessage({ command: "checkAutofillInlineMenuListFocused" }); + postWindowMessage({ command: "checkAutofillInlineMenuListFocused", token: "test-token" }); await flushPromises(); globalThis.document.dispatchEvent(new MouseEvent("mouseout")); @@ -1010,11 +1028,11 @@ describe("AutofillInlineMenuList", () => { .spyOn(autofillInlineMenuList["inlineMenuListContainer"], "querySelector") .mockReturnValue(null); - postWindowMessage({ command: "checkAutofillInlineMenuListFocused" }); + postWindowMessage({ command: "checkAutofillInlineMenuListFocused", token: "test-token" }); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "checkAutofillInlineMenuButtonFocused", portKey }, - "*", + { command: "checkAutofillInlineMenuButtonFocused", portKey, token: "test-token" }, + expectedOrigin, ); }); @@ -1022,7 +1040,7 @@ describe("AutofillInlineMenuList", () => { postWindowMessage(createInitAutofillInlineMenuListMessageMock()); const updateCiphersSpy = jest.spyOn(autofillInlineMenuList as any, "updateListItems"); - postWindowMessage({ command: "updateAutofillInlineMenuListCiphers" }); + postWindowMessage({ command: "updateAutofillInlineMenuListCiphers", token: "test-token" }); expect(updateCiphersSpy).toHaveBeenCalled(); }); @@ -1062,7 +1080,10 @@ describe("AutofillInlineMenuList", () => { postWindowMessage(createInitAutofillInlineMenuListMessageMock()); await flushPromises(); - postWindowMessage({ command: "updateAutofillInlineMenuGeneratedPassword" }); + postWindowMessage({ + command: "updateAutofillInlineMenuGeneratedPassword", + token: "test-token", + }); expect(buildColorizedPasswordElementSpy).not.toHaveBeenCalled(); }); @@ -1074,6 +1095,7 @@ describe("AutofillInlineMenuList", () => { postWindowMessage({ command: "updateAutofillInlineMenuGeneratedPassword", generatedPassword, + token: "test-token", }); expect(buildPasswordGeneratorSpy).toHaveBeenCalled(); @@ -1090,6 +1112,7 @@ describe("AutofillInlineMenuList", () => { postWindowMessage({ command: "updateAutofillInlineMenuGeneratedPassword", generatedPassword, + token: "test-token", }); expect(buildPasswordGeneratorSpy).toHaveBeenCalledTimes(1); @@ -1115,7 +1138,7 @@ describe("AutofillInlineMenuList", () => { ); await flushPromises(); - postWindowMessage({ command: "showSaveLoginInlineMenuList" }); + postWindowMessage({ command: "showSaveLoginInlineMenuList", token: "test-token" }); expect(buildSaveLoginInlineMenuSpy).not.toHaveBeenCalled(); }); @@ -1124,7 +1147,7 @@ describe("AutofillInlineMenuList", () => { postWindowMessage(createInitAutofillInlineMenuListMessageMock()); await flushPromises(); - postWindowMessage({ command: "showSaveLoginInlineMenuList" }); + postWindowMessage({ command: "showSaveLoginInlineMenuList", token: "test-token" }); expect(buildSaveLoginInlineMenuSpy).toHaveBeenCalled(); }); @@ -1143,7 +1166,7 @@ describe("AutofillInlineMenuList", () => { "setAttribute", ); - postWindowMessage({ command: "focusAutofillInlineMenuList" }); + postWindowMessage({ command: "focusAutofillInlineMenuList", token: "test-token" }); expect(inlineMenuContainerSetAttributeSpy).toHaveBeenCalledWith("role", "dialog"); expect(inlineMenuContainerSetAttributeSpy).toHaveBeenCalledWith("aria-modal", "true"); @@ -1161,7 +1184,7 @@ describe("AutofillInlineMenuList", () => { autofillInlineMenuList["inlineMenuListContainer"].querySelector("#unlock-button"); jest.spyOn(unlockButton as HTMLElement, "focus"); - postWindowMessage({ command: "focusAutofillInlineMenuList" }); + postWindowMessage({ command: "focusAutofillInlineMenuList", token: "test-token" }); expect((unlockButton as HTMLElement).focus).toBeCalled(); }); @@ -1173,7 +1196,7 @@ describe("AutofillInlineMenuList", () => { autofillInlineMenuList["inlineMenuListContainer"].querySelector("#new-item-button"); jest.spyOn(newItemButton as HTMLElement, "focus"); - postWindowMessage({ command: "focusAutofillInlineMenuList" }); + postWindowMessage({ command: "focusAutofillInlineMenuList", token: "test-token" }); expect((newItemButton as HTMLElement).focus).toBeCalled(); }); @@ -1184,7 +1207,7 @@ describe("AutofillInlineMenuList", () => { autofillInlineMenuList["inlineMenuListContainer"].querySelector(".fill-cipher-button"); jest.spyOn(firstCipherItem as HTMLElement, "focus"); - postWindowMessage({ command: "focusAutofillInlineMenuList" }); + postWindowMessage({ command: "focusAutofillInlineMenuList", token: "test-token" }); expect((firstCipherItem as HTMLElement).focus).toBeCalled(); }); @@ -1197,8 +1220,8 @@ describe("AutofillInlineMenuList", () => { globalThis.dispatchEvent(new Event("blur")); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "autofillInlineMenuBlurred", portKey }, - "*", + { command: "autofillInlineMenuBlurred", portKey, token: "test-token" }, + expectedOrigin, ); }); }); @@ -1220,8 +1243,13 @@ describe("AutofillInlineMenuList", () => { ); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "redirectAutofillInlineMenuFocusOut", direction: "previous", portKey }, - "*", + { + command: "redirectAutofillInlineMenuFocusOut", + direction: "previous", + portKey, + token: "test-token", + }, + expectedOrigin, ); }); @@ -1229,8 +1257,13 @@ describe("AutofillInlineMenuList", () => { globalThis.document.dispatchEvent(new KeyboardEvent("keydown", { code: "Tab" })); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "redirectAutofillInlineMenuFocusOut", direction: "next", portKey }, - "*", + { + command: "redirectAutofillInlineMenuFocusOut", + direction: "next", + portKey, + token: "test-token", + }, + expectedOrigin, ); }); @@ -1238,8 +1271,13 @@ describe("AutofillInlineMenuList", () => { globalThis.document.dispatchEvent(new KeyboardEvent("keydown", { code: "Escape" })); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "redirectAutofillInlineMenuFocusOut", direction: "current", portKey }, - "*", + { + command: "redirectAutofillInlineMenuFocusOut", + direction: "current", + portKey, + token: "test-token", + }, + expectedOrigin, ); }); }); @@ -1274,8 +1312,13 @@ describe("AutofillInlineMenuList", () => { autofillInlineMenuList["handleResizeObserver"](entries as unknown as ResizeObserverEntry[]); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "updateAutofillInlineMenuListHeight", styles: { height: "300px" }, portKey }, - "*", + { + command: "updateAutofillInlineMenuListHeight", + styles: { height: "300px" }, + portKey, + token: "test-token", + }, + expectedOrigin, ); }); }); diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss b/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss index 93f5f647ffe..ee9c68ee603 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss @@ -82,7 +82,7 @@ body * { width: 100%; font-family: $font-family-sans-serif; font-size: 1.6rem; - font-weight: 700; + font-weight: 500; text-align: left; background: transparent; border: none; @@ -187,7 +187,7 @@ body * { top: 0; z-index: 1; font-family: $font-family-sans-serif; - font-weight: 600; + font-weight: 500; font-size: 1rem; line-height: 1.3; letter-spacing: 0.025rem; diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.spec.ts index f7a5727e47f..e0a6e626b3c 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.spec.ts @@ -6,11 +6,13 @@ import { AutofillInlineMenuContainer } from "./autofill-inline-menu-container"; describe("AutofillInlineMenuContainer", () => { const portKey = "testPortKey"; - const iframeUrl = "https://example.com"; + const extensionOrigin = "chrome-extension://test-extension-id"; + const iframeUrl = `${extensionOrigin}/overlay/menu-list.html`; const pageTitle = "Example"; let autofillInlineMenuContainer: AutofillInlineMenuContainer; beforeEach(() => { + jest.spyOn(chrome.runtime, "getURL").mockReturnValue(`${extensionOrigin}/`); autofillInlineMenuContainer = new AutofillInlineMenuContainer(); }); @@ -28,7 +30,7 @@ describe("AutofillInlineMenuContainer", () => { portName: AutofillOverlayPort.List, }; - postWindowMessage(message); + postWindowMessage(message, extensionOrigin); expect(autofillInlineMenuContainer["defaultIframeAttributes"].src).toBe(message.iframeUrl); expect(autofillInlineMenuContainer["defaultIframeAttributes"].title).toBe(message.pageTitle); @@ -44,15 +46,48 @@ describe("AutofillInlineMenuContainer", () => { portName: AutofillOverlayPort.Button, }; - postWindowMessage(message); + postWindowMessage(message, extensionOrigin); jest.spyOn(autofillInlineMenuContainer["inlineMenuPageIframe"].contentWindow, "postMessage"); autofillInlineMenuContainer["inlineMenuPageIframe"].dispatchEvent(new Event("load")); expect(chrome.runtime.connect).toHaveBeenCalledWith({ name: message.portName }); + const expectedMessage = expect.objectContaining({ + ...message, + token: expect.any(String), + }); expect( autofillInlineMenuContainer["inlineMenuPageIframe"].contentWindow.postMessage, - ).toHaveBeenCalledWith(message, "*"); + ).toHaveBeenCalledWith(expectedMessage, "*"); + }); + + it("ignores initialization when URLs are not from extension origin", () => { + const invalidIframeUrlMessage = { + command: "initAutofillInlineMenuList", + iframeUrl: "https://malicious.com/overlay/menu-list.html", + pageTitle, + portKey, + portName: AutofillOverlayPort.List, + }; + + postWindowMessage(invalidIframeUrlMessage, extensionOrigin); + expect(autofillInlineMenuContainer["inlineMenuPageIframe"]).toBeUndefined(); + expect(autofillInlineMenuContainer["isInitialized"]).toBe(false); + + autofillInlineMenuContainer = new AutofillInlineMenuContainer(); + + const invalidStyleSheetUrlMessage = { + command: "initAutofillInlineMenuList", + iframeUrl, + pageTitle, + portKey, + portName: AutofillOverlayPort.List, + styleSheetUrl: "https://malicious.com/styles.css", + }; + + postWindowMessage(invalidStyleSheetUrlMessage, extensionOrigin); + expect(autofillInlineMenuContainer["inlineMenuPageIframe"]).toBeUndefined(); + expect(autofillInlineMenuContainer["isInitialized"]).toBe(false); }); }); @@ -69,7 +104,7 @@ describe("AutofillInlineMenuContainer", () => { portName: AutofillOverlayPort.Button, }; - postWindowMessage(message); + postWindowMessage(message, extensionOrigin); iframe = autofillInlineMenuContainer["inlineMenuPageIframe"]; jest.spyOn(iframe.contentWindow, "postMessage"); @@ -112,7 +147,8 @@ describe("AutofillInlineMenuContainer", () => { }); it("posts a message to the background from the inline menu iframe", () => { - const message = { command: "checkInlineMenuButtonFocused", portKey }; + const token = autofillInlineMenuContainer["token"]; + const message = { command: "checkInlineMenuButtonFocused", portKey, token }; postWindowMessage(message, "null", iframe.contentWindow as any); @@ -124,7 +160,62 @@ describe("AutofillInlineMenuContainer", () => { postWindowMessage(message); - expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(message, "*"); + const expectedMessage = expect.objectContaining({ + ...message, + token: expect.any(String), + }); + expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(expectedMessage, "*"); + }); + + it("ignores messages from iframe with invalid token", () => { + const message = { command: "checkInlineMenuButtonFocused", portKey, token: "invalid-token" }; + + postWindowMessage(message, "null", iframe.contentWindow as any); + + expect(port.postMessage).not.toHaveBeenCalled(); + }); + + it("ignores messages from iframe with commands not in the allowlist", () => { + const token = autofillInlineMenuContainer["token"]; + const message = { command: "maliciousCommand", portKey, token }; + + postWindowMessage(message, "null", iframe.contentWindow as any); + + expect(port.postMessage).not.toHaveBeenCalled(); + }); + }); + + describe("isExtensionUrlWithOrigin", () => { + it("validates extension URLs with matching origin", () => { + const url = "chrome-extension://test-id/path/to/file.html"; + const origin = "chrome-extension://test-id"; + + expect(autofillInlineMenuContainer["isExtensionUrlWithOrigin"](url, origin)).toBe(true); + }); + + it("rejects extension URLs with mismatched origin", () => { + const url = "chrome-extension://test-id/path/to/file.html"; + const origin = "chrome-extension://different-id"; + + expect(autofillInlineMenuContainer["isExtensionUrlWithOrigin"](url, origin)).toBe(false); + }); + + it("validates extension URL against its own origin when no expectedOrigin provided", () => { + const url = "moz-extension://test-id/path/to/file.html"; + + expect(autofillInlineMenuContainer["isExtensionUrlWithOrigin"](url)).toBe(true); + }); + + it("rejects non-extension protocols", () => { + const url = "https://example.com/path"; + const origin = "https://example.com"; + + expect(autofillInlineMenuContainer["isExtensionUrlWithOrigin"](url, origin)).toBe(false); + }); + + it("rejects empty or invalid URLs", () => { + expect(autofillInlineMenuContainer["isExtensionUrlWithOrigin"]("")).toBe(false); + expect(autofillInlineMenuContainer["isExtensionUrlWithOrigin"]("not-a-url")).toBe(false); }); }); }); diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts index 663eae9144a..6c61cfae6b4 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts @@ -1,8 +1,7 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { EVENTS } from "@bitwarden/common/autofill/constants"; -import { setElementStyles } from "../../../../utils"; +import { BrowserApi } from "../../../../../platform/browser/browser-api"; +import { generateRandomChars, setElementStyles } from "../../../../utils"; import { InitAutofillInlineMenuElementMessage, AutofillInlineMenuContainerWindowMessageHandlers, @@ -10,12 +9,37 @@ import { AutofillInlineMenuContainerPortMessage, } from "../../abstractions/autofill-inline-menu-container"; +/** + * Allowlist of commands that can be sent to the background script. + */ +const ALLOWED_BG_COMMANDS = new Set([ + "addNewVaultItem", + "autofillInlineMenuBlurred", + "autofillInlineMenuButtonClicked", + "checkAutofillInlineMenuButtonFocused", + "checkInlineMenuButtonFocused", + "fillAutofillInlineMenuCipher", + "fillGeneratedPassword", + "redirectAutofillInlineMenuFocusOut", + "refreshGeneratedPassword", + "refreshOverlayCiphers", + "triggerDelayedAutofillInlineMenuClosure", + "updateAutofillInlineMenuColorScheme", + "updateAutofillInlineMenuListHeight", + "unlockVault", + "viewSelectedCipher", +]); + export class AutofillInlineMenuContainer { private readonly setElementStyles = setElementStyles; - private readonly extensionOriginsSet: Set; private port: chrome.runtime.Port | null = null; - private portName: string; - private inlineMenuPageIframe: HTMLIFrameElement; + /** Non-null asserted. */ + private portName!: string; + /** Non-null asserted. */ + private inlineMenuPageIframe!: HTMLIFrameElement; + private token: string; + private isInitialized: boolean = false; + private readonly extensionOrigin: string; private readonly iframeStyles: Partial = { all: "initial", position: "fixed", @@ -42,16 +66,15 @@ export class AutofillInlineMenuContainer { tabIndex: "-1", }; private readonly windowMessageHandlers: AutofillInlineMenuContainerWindowMessageHandlers = { - initAutofillInlineMenuButton: (message) => this.handleInitInlineMenuIframe(message), - initAutofillInlineMenuList: (message) => this.handleInitInlineMenuIframe(message), + initAutofillInlineMenuButton: (message: InitAutofillInlineMenuElementMessage) => + this.handleInitInlineMenuIframe(message), + initAutofillInlineMenuList: (message: InitAutofillInlineMenuElementMessage) => + this.handleInitInlineMenuIframe(message), }; constructor() { - this.extensionOriginsSet = new Set([ - chrome.runtime.getURL("").slice(0, -1).toLowerCase(), // Remove the trailing slash and normalize the extension url to lowercase - "null", - ]); - + this.token = generateRandomChars(32); + this.extensionOrigin = BrowserApi.getRuntimeURL("")?.slice(0, -1); globalThis.addEventListener("message", this.handleWindowMessage); } @@ -61,9 +84,24 @@ export class AutofillInlineMenuContainer { * @param message - The message containing the iframe url and page title. */ private handleInitInlineMenuIframe(message: InitAutofillInlineMenuElementMessage) { + if (this.isInitialized) { + return; + } + + const expectedOrigin = message.extensionOrigin || this.extensionOrigin; + + if (!this.isExtensionUrlWithOrigin(message.iframeUrl, expectedOrigin)) { + return; + } + + if (message.styleSheetUrl && !this.isExtensionUrlWithOrigin(message.styleSheetUrl)) { + return; + } + this.defaultIframeAttributes.src = message.iframeUrl; this.defaultIframeAttributes.title = message.pageTitle; this.portName = message.portName; + this.isInitialized = true; this.inlineMenuPageIframe = globalThis.document.createElement("iframe"); this.setElementStyles(this.inlineMenuPageIframe, this.iframeStyles, true); @@ -79,6 +117,31 @@ export class AutofillInlineMenuContainer { globalThis.document.body.appendChild(this.inlineMenuPageIframe); } + /** + * Validates that a URL uses an extension protocol and matches the expected extension origin. + * If no expectedOrigin is provided, validates against the URL's own origin. + * + * @param url - The URL to validate. + */ + private isExtensionUrlWithOrigin(url: string, expectedOrigin?: string): boolean { + if (!url) { + return false; + } + try { + const urlObj = new URL(url); + const isExtensionProtocol = /^[a-z]+(-[a-z]+)?-extension:$/i.test(urlObj.protocol); + + if (!isExtensionProtocol) { + return false; + } + + const originToValidate = expectedOrigin ?? urlObj.origin; + return urlObj.origin === originToValidate || urlObj.href.startsWith(originToValidate + "/"); + } catch { + return false; + } + } + /** * Sets up the port message listener for the inline menu page. * @@ -86,7 +149,8 @@ export class AutofillInlineMenuContainer { */ private setupPortMessageListener = (message: InitAutofillInlineMenuElementMessage) => { this.port = chrome.runtime.connect({ name: this.portName }); - this.postMessageToInlineMenuPage(message); + const initMessage = { ...message, token: this.token }; + this.postMessageToInlineMenuPageUnsafe(initMessage); }; /** @@ -95,6 +159,22 @@ export class AutofillInlineMenuContainer { * @param message - The message to post. */ private postMessageToInlineMenuPage(message: AutofillInlineMenuContainerWindowMessage) { + if (this.inlineMenuPageIframe?.contentWindow) { + const messageWithToken = { ...message, token: this.token }; + this.postMessageToInlineMenuPageUnsafe(messageWithToken); + } + } + + /** + * Posts a message to the inline menu page iframe without token validation. + * + * UNSAFE: Bypasses token authentication and sends raw messages. Only use internally + * when sending trusted messages (e.g., initialization) or when token validation + * would create circular dependencies. External callers should use postMessageToInlineMenuPage(). + * + * @param message - The message to post. + */ + private postMessageToInlineMenuPageUnsafe(message: Record) { if (this.inlineMenuPageIframe?.contentWindow) { this.inlineMenuPageIframe.contentWindow.postMessage(message, "*"); } @@ -106,9 +186,15 @@ export class AutofillInlineMenuContainer { * @param message - The message to post. */ private postMessageToBackground(message: AutofillInlineMenuContainerPortMessage) { - if (this.port) { - this.port.postMessage(message); + if (!this.port) { + return; } + + if (message.command && !ALLOWED_BG_COMMANDS.has(message.command)) { + return; + } + + this.port.postMessage(message); } /** @@ -116,23 +202,42 @@ export class AutofillInlineMenuContainer { * * @param event - The message event. */ - private handleWindowMessage = (event: MessageEvent) => { + private handleWindowMessage = (event: MessageEvent) => { const message = event.data; + if (!message?.command) { + return; + } if (this.isForeignWindowMessage(event)) { return; } if (this.windowMessageHandlers[message.command]) { + // only accept init messages from extension origin or parent window + if ( + (message.command === "initAutofillInlineMenuButton" || + message.command === "initAutofillInlineMenuList") && + !this.isMessageFromExtensionOrigin(event) && + !this.isMessageFromParentWindow(event) + ) { + return; + } this.windowMessageHandlers[message.command](message); return; } if (this.isMessageFromParentWindow(event)) { + // messages from parent window are trusted and forwarded to iframe this.postMessageToInlineMenuPage(message); return; } - this.postMessageToBackground(message); + // messages from iframe to background require object identity verification with a contentWindow check and token auth + if (this.isMessageFromInlineMenuPageIframe(event)) { + if (this.isValidSessionToken(message)) { + this.postMessageToBackground(message); + } + return; + } }; /** @@ -142,8 +247,8 @@ export class AutofillInlineMenuContainer { * * @param event - The message event. */ - private isForeignWindowMessage(event: MessageEvent) { - if (!event.data.portKey) { + private isForeignWindowMessage(event: MessageEvent) { + if (!event.data?.portKey) { return true; } @@ -159,7 +264,9 @@ export class AutofillInlineMenuContainer { * * @param event - The message event. */ - private isMessageFromParentWindow(event: MessageEvent): boolean { + private isMessageFromParentWindow( + event: MessageEvent, + ): boolean { return globalThis.parent === event.source; } @@ -168,14 +275,43 @@ export class AutofillInlineMenuContainer { * * @param event - The message event. */ - private isMessageFromInlineMenuPageIframe(event: MessageEvent): boolean { + private isMessageFromInlineMenuPageIframe( + event: MessageEvent, + ): boolean { if (!this.inlineMenuPageIframe) { return false; } + // only trust the specific iframe we created + return this.inlineMenuPageIframe.contentWindow === event.source; + } - return ( - this.inlineMenuPageIframe.contentWindow === event.source && - this.extensionOriginsSet.has(event.origin.toLowerCase()) - ); + /** + * Validates that the message contains a valid session token. + * The session token is generated when the container is created and is refreshed + * every time the inline menu container is recreated. + * + */ + private isValidSessionToken(message: { token: string }): boolean { + if (!this.token || !message?.token || !message?.token.length) { + return false; + } + return message.token === this.token; + } + + /** + * Validates that a message event originates from the extension. + * + * @param event - The message event to validate. + * @returns True if the message is from the extension origin. + */ + private isMessageFromExtensionOrigin(event: MessageEvent): boolean { + try { + if (event.origin === "null") { + return false; + } + return event.origin === this.extensionOrigin; + } catch { + return false; + } } } diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts index 950676cf202..5df6e7cd190 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { EVENTS } from "@bitwarden/common/autofill/constants"; import { RedirectFocusDirection } from "../../../../enums/autofill-overlay.enum"; @@ -10,10 +8,15 @@ import { export class AutofillInlineMenuPageElement extends HTMLElement { protected shadowDom: ShadowRoot; - protected messageOrigin: string; - protected translations: Record; - private portKey: string; - protected windowMessageHandlers: AutofillInlineMenuPageElementWindowMessageHandlers; + /** Non-null asserted. */ + protected messageOrigin!: string; + /** Non-null asserted. */ + protected translations!: Record; + /** Non-null asserted. */ + private portKey!: string; + /** Non-null asserted. */ + protected windowMessageHandlers!: AutofillInlineMenuPageElementWindowMessageHandlers; + private token?: string; constructor() { super(); @@ -56,7 +59,16 @@ export class AutofillInlineMenuPageElement extends HTMLElement { * @param message - The message to post */ protected postMessageToParent(message: AutofillInlineMenuPageElementWindowMessage) { - globalThis.parent.postMessage({ portKey: this.portKey, ...message }, "*"); + // never send messages containing authentication tokens without a valid token and an established messageOrigin + if (!this.token || !this.messageOrigin) { + return; + } + const messageWithAuth: Record = { + portKey: this.portKey, + ...message, + token: this.token, + }; + globalThis.parent.postMessage(messageWithAuth, this.messageOrigin); } /** @@ -94,6 +106,10 @@ export class AutofillInlineMenuPageElement extends HTMLElement { return; } + if (event.source !== globalThis.parent) { + return; + } + if (!this.messageOrigin) { this.messageOrigin = event.origin; } @@ -103,6 +119,26 @@ export class AutofillInlineMenuPageElement extends HTMLElement { } const message = event?.data; + + if (!message?.command) { + return; + } + + const isInitCommand = + message.command === "initAutofillInlineMenuButton" || + message.command === "initAutofillInlineMenuList"; + + if (isInitCommand) { + if (!message?.token) { + return; + } + this.token = message.token; + } else { + if (!this.token || !message?.token || message.token !== this.token) { + return; + } + } + const handler = this.windowMessageHandlers[message?.command]; if (!handler) { return; diff --git a/apps/browser/src/autofill/overlay/notifications/content/__snapshots__/overlay-notifications-content.service.spec.ts.snap b/apps/browser/src/autofill/overlay/notifications/content/__snapshots__/overlay-notifications-content.service.spec.ts.snap index 39ca68d912c..cfcedc9da7a 100644 --- a/apps/browser/src/autofill/overlay/notifications/content/__snapshots__/overlay-notifications-content.service.spec.ts.snap +++ b/apps/browser/src/autofill/overlay/notifications/content/__snapshots__/overlay-notifications-content.service.spec.ts.snap @@ -7,7 +7,7 @@ exports[`OverlayNotificationsContentService opening the notification bar creates >