diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml
index 823cb7e25e0..bece680b9d0 100644
--- a/.github/workflows/build-browser.yml
+++ b/.github/workflows/build-browser.yml
@@ -147,7 +147,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -248,7 +248,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -359,7 +359,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml
index 22ba3a3e7be..d1df280f764 100644
--- a/.github/workflows/build-cli.yml
+++ b/.github/workflows/build-cli.yml
@@ -124,7 +124,7 @@ jobs:
awk '{print tolower($0)}')" >> $GITHUB_ENV
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -302,7 +302,7 @@ jobs:
choco install nasm --no-progress
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml
index b1e0232cb9c..b33b128ea8e 100644
--- a/.github/workflows/build-desktop.yml
+++ b/.github/workflows/build-desktop.yml
@@ -174,7 +174,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -323,7 +323,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -429,7 +429,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -665,6 +665,239 @@ jobs:
path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml
if-no-files-found: error
+ windows-beta:
+ name: Windows Beta Build
+ runs-on: windows-2022
+ needs: setup
+ permissions:
+ contents: read
+ id-token: write
+ defaults:
+ run:
+ shell: pwsh
+ working-directory: apps/desktop
+ env:
+ _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
+ _NODE_VERSION: ${{ needs.setup.outputs.node_version }}
+ NODE_OPTIONS: --max_old_space_size=4096
+ steps:
+ - name: Check out repo
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: Set up Node
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
+ with:
+ cache: 'npm'
+ cache-dependency-path: '**/package-lock.json'
+ node-version: ${{ env._NODE_VERSION }}
+
+ - name: Install AST
+ run: dotnet tool install --global AzureSignTool --version 4.0.1
+
+ - name: Print environment
+ run: |
+ node --version
+ npm --version
+ choco --version
+ rustup show
+
+ - name: Log in to Azure
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: bitwarden/gh-actions/azure-login@main
+ with:
+ subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ tenant_id: ${{ secrets.AZURE_TENANT_ID }}
+ client_id: ${{ secrets.AZURE_CLIENT_ID }}
+
+ - name: Retrieve secrets
+ id: retrieve-secrets
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: bitwarden/gh-actions/get-keyvault-secrets@main
+ with:
+ keyvault: "bitwarden-ci"
+ secrets: "code-signing-vault-url,
+ code-signing-client-id,
+ code-signing-tenant-id,
+ code-signing-client-secret,
+ code-signing-cert-name"
+
+ - name: Log out from Azure
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: bitwarden/gh-actions/azure-logout@main
+
+ - name: Install Node dependencies
+ run: npm ci
+ working-directory: ./
+
+ - name: Download SDK Artifacts
+ if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }}
+ uses: bitwarden/gh-actions/download-artifacts@main
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ workflow: build-wasm-internal.yml
+ workflow_conclusion: success
+ branch: ${{ inputs.sdk_branch }}
+ artifacts: sdk-internal
+ repo: bitwarden/sdk-internal
+ path: ../sdk-internal
+ if_no_artifact_found: fail
+
+ - name: Override SDK
+ if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }}
+ working-directory: ./
+ run: |
+ ls -l ../
+ npm link ../sdk-internal
+
+ - name: Cache Native Module
+ uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
+ id: cache
+ with:
+ path: |
+ apps/desktop/desktop_native/napi/*.node
+ apps/desktop/desktop_native/dist/*
+ key: rust-${{ runner.os }}-${{ hashFiles('apps/desktop/desktop_native/**/*') }}
+
+ - name: Build Native Module
+ if: steps.cache.outputs.cache-hit != 'true'
+ working-directory: apps/desktop/desktop_native
+ run: node build.js cross-platform
+
+ - name: Build
+ run: npm run build
+
+ - name: Pack
+ if: ${{ needs.setup.outputs.has_secrets == 'false' }}
+ run: npm run pack:win:beta
+
+ - name: Pack & Sign
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ env:
+ ELECTRON_BUILDER_SIGN: 1
+ SIGNING_VAULT_URL: ${{ steps.retrieve-secrets.outputs.code-signing-vault-url }}
+ SIGNING_CLIENT_ID: ${{ steps.retrieve-secrets.outputs.code-signing-client-id }}
+ SIGNING_TENANT_ID: ${{ steps.retrieve-secrets.outputs.code-signing-tenant-id }}
+ SIGNING_CLIENT_SECRET: ${{ steps.retrieve-secrets.outputs.code-signing-client-secret }}
+ SIGNING_CERT_NAME: ${{ steps.retrieve-secrets.outputs.code-signing-cert-name }}
+ run: npm run pack:win:beta
+
+ - 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"
+
+ - 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
+
+ - name: Upload portable exe artifact
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
+ with:
+ name: Bitwarden-Beta-Portable-${{ env._PACKAGE_VERSION }}.exe
+ path: apps/desktop/dist/Bitwarden-Beta-Portable-${{ env._PACKAGE_VERSION }}.exe
+ if-no-files-found: error
+
+ - name: Upload installer exe artifact
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
+ with:
+ name: Bitwarden-Beta-Installer-${{ env._PACKAGE_VERSION }}.exe
+ path: apps/desktop/dist/nsis-web/Bitwarden-Beta-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
+ with:
+ name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32.appx
+ path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32.appx
+ if-no-files-found: error
+
+ - name: Upload store appx ia32 artifact
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
+ with:
+ name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32-store.appx
+ path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32-store.appx
+ if-no-files-found: error
+
+ - name: Upload NSIS ia32 artifact
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.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
+ if-no-files-found: error
+
+ - name: Upload appx x64 artifact
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
+ with:
+ name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64.appx
+ path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64.appx
+ if-no-files-found: error
+
+ - name: Upload store appx x64 artifact
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
+ with:
+ name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64-store.appx
+ path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64-store.appx
+ if-no-files-found: error
+
+ - name: Upload NSIS x64 artifact
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.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
+ if-no-files-found: error
+
+ - name: Upload appx ARM64 artifact
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
+ with:
+ name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64.appx
+ path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64.appx
+ if-no-files-found: error
+
+ - name: Upload store appx ARM64 artifact
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
+ with:
+ name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64-store.appx
+ path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64-store.appx
+ if-no-files-found: error
+
+ - name: Upload NSIS ARM64 artifact
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.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
+ if-no-files-found: error
+
+ - name: Upload auto-update artifact
+ if: ${{ needs.setup.outputs.has_secrets == 'true' }}
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
+ with:
+ name: ${{ needs.setup.outputs.release_channel }}-beta.yml
+ path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml
+ if-no-files-found: error
+
macos-build:
name: MacOS Build
@@ -688,7 +921,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -920,7 +1153,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -1184,7 +1417,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml
index dc4da7d37de..2b7b6394f24 100644
--- a/.github/workflows/chromatic.yml
+++ b/.github/workflows/chromatic.yml
@@ -57,7 +57,7 @@ jobs:
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version: ${{ steps.retrieve-node-version.outputs.node_version }}
if: steps.get-changed-files-for-chromatic.outputs.storyFiles == 'true'
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 738aef899f5..14b5d51d9ef 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -61,7 +61,7 @@ jobs:
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/.github/workflows/nx.yml b/.github/workflows/nx.yml
index 9349239a134..526c2b5d864 100644
--- a/.github/workflows/nx.yml
+++ b/.github/workflows/nx.yml
@@ -25,7 +25,7 @@ jobs:
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/.github/workflows/publish-cli.yml b/.github/workflows/publish-cli.yml
index 4f776876f17..121236c0deb 100644
--- a/.github/workflows/publish-cli.yml
+++ b/.github/workflows/publish-cli.yml
@@ -205,7 +205,7 @@ jobs:
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version: ${{ steps.retrieve-node-version.outputs.node_version }}
npm-version: "11.5.1" # FIXME: npm 11.5.1 or later is required to publish w/ OIDC; move version management to somewhere maintainable by automation
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 2770c1257ea..680bfb87cfe 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -34,7 +34,7 @@ jobs:
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/CLAUDE.md b/CLAUDE.md
index 9739288aac8..dd3b6445edd 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -96,3 +96,13 @@ enum CipherType {
```
Example: `/libs/common/src/vault/enums/cipher-type.ts`
+
+## References
+
+- [Web Clients Architecture](https://contributing.bitwarden.com/architecture/clients)
+- [Architectural Decision Records (ADRs)](https://contributing.bitwarden.com/architecture/adr/)
+- [Contributing Guide](https://contributing.bitwarden.com/)
+- [Web Clients Setup Guide](https://contributing.bitwarden.com/getting-started/clients/)
+- [Code Style](https://contributing.bitwarden.com/contributing/code-style/)
+- [Security Whitepaper](https://bitwarden.com/help/bitwarden-security-white-paper/)
+- [Security Definitions](https://contributing.bitwarden.com/architecture/security/definitions)
diff --git a/apps/browser/project.json b/apps/browser/project.json
new file mode 100644
index 00000000000..9a8df56c170
--- /dev/null
+++ b/apps/browser/project.json
@@ -0,0 +1,494 @@
+{
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
+ "name": "browser",
+ "projectType": "application",
+ "sourceRoot": "apps/browser/src",
+ "tags": ["scope:browser", "type:app"],
+ "targets": {
+ "build": {
+ "executor": "@nx/webpack:webpack",
+ "outputs": ["{options.outputPath}"],
+ "defaultConfiguration": "chrome-dev",
+ "options": {
+ "outputPath": "dist/apps/browser",
+ "webpackConfig": "apps/browser/webpack.config.js",
+ "tsConfig": "apps/browser/tsconfig.json",
+ "main": "apps/browser/src/popup/main.ts",
+ "target": "web",
+ "compiler": "tsc"
+ },
+ "configurations": {
+ "chrome": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/chrome",
+ "env": {
+ "BROWSER": "chrome",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "production"
+ }
+ },
+ "chrome-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/chrome-dev",
+ "env": {
+ "BROWSER": "chrome",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "edge": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/edge",
+ "env": {
+ "BROWSER": "edge",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "production"
+ }
+ },
+ "edge-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/edge-dev",
+ "env": {
+ "BROWSER": "edge",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "firefox": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/firefox",
+ "env": {
+ "BROWSER": "firefox",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "production"
+ }
+ },
+ "firefox-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/firefox-dev",
+ "env": {
+ "BROWSER": "firefox",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "firefox-mv2": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/firefox-mv2",
+ "env": {
+ "BROWSER": "firefox",
+ "MANIFEST_VERSION": "2",
+ "NODE_ENV": "production"
+ }
+ },
+ "firefox-mv2-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/firefox-mv2-dev",
+ "env": {
+ "BROWSER": "firefox",
+ "MANIFEST_VERSION": "2",
+ "NODE_ENV": "development"
+ }
+ },
+ "opera": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/opera",
+ "env": {
+ "BROWSER": "opera",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "production"
+ }
+ },
+ "opera-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/opera-dev",
+ "env": {
+ "BROWSER": "opera",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "safari": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/safari",
+ "env": {
+ "BROWSER": "safari",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "production"
+ }
+ },
+ "safari-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/safari-dev",
+ "env": {
+ "BROWSER": "safari",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "safari-mv2": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/safari-mv2",
+ "env": {
+ "BROWSER": "safari",
+ "MANIFEST_VERSION": "2",
+ "NODE_ENV": "production"
+ }
+ },
+ "safari-mv2-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/safari-mv2-dev",
+ "env": {
+ "BROWSER": "safari",
+ "MANIFEST_VERSION": "2",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-chrome": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/commercial-chrome",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "chrome",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "production"
+ }
+ },
+ "commercial-chrome-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-chrome-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "chrome",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-edge": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/commercial-edge",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "edge",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "production"
+ }
+ },
+ "commercial-edge-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-edge-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "edge",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-firefox": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/commercial-firefox",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "firefox",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "production"
+ }
+ },
+ "commercial-firefox-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-firefox-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "firefox",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-firefox-mv2": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/commercial-firefox-mv2",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "firefox",
+ "MANIFEST_VERSION": "2",
+ "NODE_ENV": "production"
+ }
+ },
+ "commercial-firefox-mv2-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-firefox-mv2-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "firefox",
+ "MANIFEST_VERSION": "2",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-opera": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/commercial-opera",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "opera",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "production"
+ }
+ },
+ "commercial-opera-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-opera-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "opera",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-safari": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/commercial-safari",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "safari",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "production"
+ }
+ },
+ "commercial-safari-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-safari-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "safari",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-safari-mv2": {
+ "mode": "production",
+ "outputPath": "dist/apps/browser/commercial-safari-mv2",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "safari",
+ "MANIFEST_VERSION": "2",
+ "NODE_ENV": "production"
+ }
+ },
+ "commercial-safari-mv2-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-safari-mv2-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "safari",
+ "MANIFEST_VERSION": "2",
+ "NODE_ENV": "development"
+ }
+ }
+ }
+ },
+ "serve": {
+ "executor": "@nx/webpack:webpack",
+ "defaultConfiguration": "chrome-dev",
+ "options": {
+ "outputPath": "dist/apps/browser",
+ "webpackConfig": "apps/browser/webpack.config.js",
+ "tsConfig": "apps/browser/tsconfig.json",
+ "main": "apps/browser/src/popup/main.ts",
+ "target": "web",
+ "compiler": "tsc",
+ "watch": true
+ },
+ "configurations": {
+ "chrome-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/chrome-dev",
+ "env": {
+ "BROWSER": "chrome",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "firefox-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/firefox-dev",
+ "env": {
+ "BROWSER": "firefox",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "firefox-mv2-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/firefox-mv2-dev",
+ "env": {
+ "BROWSER": "firefox",
+ "MANIFEST_VERSION": "2",
+ "NODE_ENV": "development"
+ }
+ },
+ "safari-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/safari-dev",
+ "env": {
+ "BROWSER": "safari",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "safari-mv2-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/safari-mv2-dev",
+ "env": {
+ "BROWSER": "safari",
+ "MANIFEST_VERSION": "2",
+ "NODE_ENV": "development"
+ }
+ },
+ "edge-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/edge-dev",
+ "env": {
+ "BROWSER": "edge",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "opera-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/opera-dev",
+ "env": {
+ "BROWSER": "opera",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-chrome-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-chrome-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "chrome",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-firefox-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-firefox-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "firefox",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-firefox-mv2-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-firefox-mv2-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "firefox",
+ "MANIFEST_VERSION": "2",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-safari-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-safari-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "safari",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-safari-mv2-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-safari-mv2-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "safari",
+ "MANIFEST_VERSION": "2",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-edge-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-edge-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "edge",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ },
+ "commercial-opera-dev": {
+ "mode": "development",
+ "outputPath": "dist/apps/browser/commercial-opera-dev",
+ "webpackConfig": "bitwarden_license/bit-browser/webpack.config.js",
+ "main": "bitwarden_license/bit-browser/src/popup/main.ts",
+ "tsConfig": "bitwarden_license/bit-browser/tsconfig.json",
+ "env": {
+ "BROWSER": "opera",
+ "MANIFEST_VERSION": "3",
+ "NODE_ENV": "development"
+ }
+ }
+ }
+ },
+ "test": {
+ "executor": "@nx/jest:jest",
+ "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
+ "options": {
+ "jestConfig": "apps/browser/jest.config.js"
+ }
+ },
+ "lint": {
+ "executor": "@nx/eslint:lint",
+ "outputs": ["{options.outputFile}"],
+ "options": {
+ "lintFilePatterns": ["apps/browser/**/*.ts", "apps/browser/**/*.html"]
+ }
+ }
+ }
+}
diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index d91a33c6796..9a430654a0a 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -558,7 +558,7 @@
"message": "Archive",
"description": "Verb"
},
- "unarchive": {
+ "unArchive": {
"message": "Unarchive"
},
"itemsInArchive": {
@@ -570,11 +570,11 @@
"noItemsInArchiveDesc": {
"message": "Archived items will appear here and will be excluded from general search results and autofill suggestions."
},
- "itemSentToArchive": {
- "message": "Item sent to archive"
+ "itemWasSentToArchive": {
+ "message": "Item was sent to archive"
},
- "itemRemovedFromArchive": {
- "message": "Item removed from archive"
+ "itemUnarchived": {
+ "message": "Item was unarchived"
},
"archiveItem": {
"message": "Archive item"
@@ -5579,17 +5579,37 @@
"hasItemsVaultNudgeTitle": {
"message": "Welcome to your vault!"
},
- "phishingPageTitle":{
- "message": "Phishing website"
+ "phishingPageTitleV2":{
+ "message": "Phishing attempt detected"
},
- "phishingPageCloseTab": {
- "message": "Close tab"
+ "phishingPageSummary": {
+ "message": "The site you are attempting to visit is a known malicious site and a security risk."
},
- "phishingPageContinue": {
- "message": "Continue"
+ "phishingPageCloseTabV2": {
+ "message": "Close this tab"
},
- "phishingPageLearnWhy": {
- "message": "Why are you seeing this?"
+ "phishingPageContinueV2": {
+ "message": "Continue to this site (not recommended)"
+ },
+ "phishingPageExplanation1": {
+ "message": "This site was found in ",
+ "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.",
+ "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"
+ },
+ "protectedBy": {
+ "message": "Protected by $PRODUCT$",
+ "placeholders": {
+ "product": {
+ "content": "$1",
+ "example": "Bitwarden Phishing Blocker"
+ }
+ }
},
"hasItemsVaultNudgeBodyOne": {
"message": "Autofill items for the current page"
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 0c9b4634569..a9f43045902 100644
--- a/apps/browser/src/auth/popup/settings/account-security.component.ts
+++ b/apps/browser/src/auth/popup/settings/account-security.component.ts
@@ -172,7 +172,9 @@ export class AccountSecurityComponent implements OnInit, OnDestroy {
}
const showOnLocked =
- !this.platformUtilsService.isFirefox() && !this.platformUtilsService.isSafari();
+ !this.platformUtilsService.isFirefox() &&
+ !this.platformUtilsService.isSafari() &&
+ !(this.platformUtilsService.isOpera() && navigator.platform === "MacIntel");
this.vaultTimeoutOptions = [
{ name: this.i18nService.t("immediately"), value: 0 },
diff --git a/apps/browser/src/autofill/services/autofill.service.spec.ts b/apps/browser/src/autofill/services/autofill.service.spec.ts
index f0ae8856ecd..9b0424c5cdf 100644
--- a/apps/browser/src/autofill/services/autofill.service.spec.ts
+++ b/apps/browser/src/autofill/services/autofill.service.spec.ts
@@ -4105,6 +4105,7 @@ describe("AutofillService", () => {
});
it("returns null if the field cannot be hidden", () => {
+ usernameField.form = "differentFormId";
const result = autofillService["findUsernameField"](
pageDetails,
passwordField,
@@ -4116,6 +4117,18 @@ describe("AutofillService", () => {
expect(result).toBe(null);
});
+ it("returns the field if the username field is in the form", () => {
+ const result = autofillService["findUsernameField"](
+ pageDetails,
+ passwordField,
+ false,
+ false,
+ false,
+ );
+
+ expect(result).toBe(usernameField);
+ });
+
it("returns the field if the field can be hidden", () => {
const result = autofillService["findUsernameField"](
pageDetails,
diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts
index 73262962dbc..ea0fb089690 100644
--- a/apps/browser/src/autofill/services/autofill.service.ts
+++ b/apps/browser/src/autofill/services/autofill.service.ts
@@ -2286,11 +2286,16 @@ export default class AutofillService implements AutofillServiceInterface {
this.findMatchingFieldIndex(f, AutoFillConstants.UsernameFieldNames) > -1;
const isInSameForm = f.form === passwordField.form;
+ // An email or tel field in the same form as the password field is likely a qualified
+ // candidate for autofill, even if visibility checks are unreliable
+ const isQualifiedUsernameField =
+ f.form === passwordField.form && (f.type === "email" || f.type === "tel");
+
if (
!f.disabled &&
(canBeReadOnly || !f.readonly) &&
(withoutForm || isInSameForm || includesUsernameFieldName) &&
- (canBeHidden || f.viewable) &&
+ (canBeHidden || f.viewable || isQualifiedUsernameField) &&
(f.type === "text" || f.type === "email" || f.type === "tel")
) {
// Prioritize fields in the same form as the password field
diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts
index bd28ddfbbbf..4cd61ebead1 100644
--- a/apps/browser/src/background/main.background.ts
+++ b/apps/browser/src/background/main.background.ts
@@ -100,6 +100,8 @@ import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-
import { MasterPasswordService } from "@bitwarden/common/key-management/master-password/services/master-password.service";
import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
import { PinService } from "@bitwarden/common/key-management/pin/pin.service.implementation";
+import { SecurityStateService } from "@bitwarden/common/key-management/security-state/abstractions/security-state.service";
+import { DefaultSecurityStateService } from "@bitwarden/common/key-management/security-state/services/security-state.service";
import { DefaultProcessReloadService } from "@bitwarden/common/key-management/services/default-process-reload.service";
import {
DefaultVaultTimeoutSettingsService,
@@ -452,6 +454,7 @@ export default class MainBackground {
taskService: TaskService;
cipherEncryptionService: CipherEncryptionService;
private restrictedItemTypesService: RestrictedItemTypesService;
+ private securityStateService: SecurityStateService;
ipcContentScriptManagerService: IpcContentScriptManagerService;
ipcService: IpcService;
@@ -668,6 +671,8 @@ export default class MainBackground {
logoutCallback,
);
+ this.securityStateService = new DefaultSecurityStateService(this.stateProvider);
+
this.popupViewCacheBackgroundService = new PopupViewCacheBackgroundService(
messageListener,
this.globalStateProvider,
@@ -830,6 +835,7 @@ export default class MainBackground {
this.accountService,
this.kdfConfigService,
this.keyService,
+ this.securityStateService,
this.apiService,
this.stateProvider,
this.configService,
@@ -984,6 +990,7 @@ export default class MainBackground {
this.sendStateProvider = new SendStateProvider(this.stateProvider);
this.sendService = new SendService(
+ this.accountService,
this.keyService,
this.i18nService,
this.keyGenerationService,
@@ -999,7 +1006,6 @@ export default class MainBackground {
this.avatarService = new AvatarService(this.apiService, this.stateProvider);
this.providerService = new ProviderService(this.stateProvider);
-
this.syncService = new DefaultSyncService(
this.masterPasswordService,
this.accountService,
@@ -1025,6 +1031,7 @@ export default class MainBackground {
this.tokenService,
this.authService,
this.stateProvider,
+ this.securityStateService,
);
this.syncServiceListener = new SyncServiceListener(
diff --git a/apps/browser/src/dirt/phishing-detection/pages/learn-more-component.html b/apps/browser/src/dirt/phishing-detection/pages/learn-more-component.html
deleted file mode 100644
index 5ea79c3f840..00000000000
--- a/apps/browser/src/dirt/phishing-detection/pages/learn-more-component.html
+++ /dev/null
@@ -1,4 +0,0 @@
-{{ "phishingPageLearnWhy"| i18n}}
-
- {{ "learnMore" | i18n }}
-
diff --git a/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.component.html b/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.component.html
index f6e3baf8766..5cac567c5c3 100644
--- a/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.component.html
+++ b/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.component.html
@@ -1,13 +1,46 @@
-
-
- {{ "phishingPageTitle" | i18n }}
-
-
+
+
+
+
{{ "phishingPageTitleV2" | i18n }}
+
-
-
+
+
+
{{ "phishingPageSummary" | i18n }}
+
+
+ {{ phishingHost$ | async }}
+
+
+
+
+ {{ "phishingPageExplanation1" | i18n }}Phishing.Database{{ "phishingPageExplanation2" | i18n }}
+
+
+
+ {{ "phishingPageLearnMore" | i18n }}
+
+
+
+
+
+
+
diff --git a/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.component.ts b/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.component.ts
index dc6ab2d329e..4712c94c89e 100644
--- a/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.component.ts
+++ b/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.component.ts
@@ -1,10 +1,10 @@
// eslint-disable-next-line no-restricted-imports
import { CommonModule } from "@angular/common";
// eslint-disable-next-line no-restricted-imports
-import { Component, OnDestroy } from "@angular/core";
+import { Component, inject } from "@angular/core";
// eslint-disable-next-line no-restricted-imports
import { ActivatedRoute, RouterModule } from "@angular/router";
-import { Subject, takeUntil } from "rxjs";
+import { map } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
@@ -13,12 +13,16 @@ import {
CheckboxModule,
FormFieldModule,
IconModule,
+ IconTileComponent,
LinkModule,
+ CalloutComponent,
+ TypographyModule,
} from "@bitwarden/components";
import { PhishingDetectionService } from "../services/phishing-detection.service";
@Component({
+ selector: "dirt-phishing-warning",
standalone: true,
templateUrl: "phishing-warning.component.html",
imports: [
@@ -31,18 +35,16 @@ import { PhishingDetectionService } from "../services/phishing-detection.service
CheckboxModule,
ButtonModule,
RouterModule,
+ IconTileComponent,
+ CalloutComponent,
+ TypographyModule,
],
})
-export class PhishingWarning implements OnDestroy {
- phishingHost = "";
-
- private destroy$ = new Subject
();
-
- constructor(private activatedRoute: ActivatedRoute) {
- this.activatedRoute.queryParamMap.pipe(takeUntil(this.destroy$)).subscribe((params) => {
- this.phishingHost = params.get("phishingHost") || "";
- });
- }
+export class PhishingWarning {
+ private activatedRoute = inject(ActivatedRoute);
+ protected phishingHost$ = this.activatedRoute.queryParamMap.pipe(
+ map((params) => params.get("phishingHost") || ""),
+ );
async closeTab() {
await PhishingDetectionService.requestClosePhishingWarningPage();
@@ -50,9 +52,4 @@ export class PhishingWarning implements OnDestroy {
async continueAnyway() {
await PhishingDetectionService.requestContinueToDangerousUrl();
}
-
- ngOnDestroy(): void {
- this.destroy$.next();
- this.destroy$.complete();
- }
}
diff --git a/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.stories.ts b/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.stories.ts
new file mode 100644
index 00000000000..30d3b7faeee
--- /dev/null
+++ b/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.stories.ts
@@ -0,0 +1,137 @@
+// TODO: This needs to be dealt with by moving this folder or updating the lint rule.
+/* eslint-disable no-restricted-imports */
+import { ActivatedRoute, RouterModule } from "@angular/router";
+import { Meta, StoryObj, moduleMetadata } from "@storybook/angular";
+import { BehaviorSubject, of } from "rxjs";
+
+import { DeactivatedOrg } from "@bitwarden/assets/svg";
+import { ClientType } from "@bitwarden/common/enums";
+import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+import { AnonLayoutComponent, I18nMockService } from "@bitwarden/components";
+
+import { PhishingWarning } from "./phishing-warning.component";
+import { ProtectedByComponent } from "./protected-by-component";
+
+class MockPlatformUtilsService implements Partial {
+ getApplicationVersion = () => Promise.resolve("Version 2024.1.1");
+ getClientType = () => ClientType.Web;
+}
+
+/**
+ * Helper function to create ActivatedRoute mock with query parameters
+ */
+function mockActivatedRoute(queryParams: Record) {
+ return {
+ provide: ActivatedRoute,
+ useValue: {
+ queryParamMap: of({
+ get: (key: string) => queryParams[key] || null,
+ }),
+ queryParams: of(queryParams),
+ },
+ };
+}
+
+type StoryArgs = {
+ phishingHost: string;
+};
+
+export default {
+ title: "Browser/DIRT/Phishing Warning",
+ component: PhishingWarning,
+ decorators: [
+ moduleMetadata({
+ imports: [AnonLayoutComponent, ProtectedByComponent, RouterModule],
+ providers: [
+ {
+ provide: PlatformUtilsService,
+ useClass: MockPlatformUtilsService,
+ },
+ {
+ provide: I18nService,
+ useFactory: () =>
+ new I18nMockService({
+ accessing: "Accessing",
+ appLogoLabel: "Bitwarden logo",
+ phishingPageTitleV2: "Phishing attempt detected",
+ phishingPageCloseTabV2: "Close this tab",
+ phishingPageSummary:
+ "The site you are attempting to visit is a known malicious site and a security risk.",
+ phishingPageContinueV2: "Continue to this site (not recommended)",
+ phishingPageExplanation1: "This site was found in ",
+ phishingPageExplanation2:
+ ", an open-source list of known phishing sites used for stealing personal and sensitive information.",
+ phishingPageLearnMore: "Learn more about phishing detection",
+ protectedBy: (product) => `Protected by ${product}`,
+ learnMore: "Learn more",
+ danger: "error",
+ }),
+ },
+ {
+ provide: EnvironmentService,
+ useValue: {
+ environment$: new BehaviorSubject({
+ getHostname() {
+ return "bitwarden.com";
+ },
+ }).asObservable(),
+ },
+ },
+ mockActivatedRoute({ phishingHost: "malicious-example.com" }),
+ ],
+ }),
+ ],
+ render: (args) => ({
+ props: args,
+ template: /*html*/ `
+
+
+
+
+ `,
+ }),
+ argTypes: {
+ phishingHost: {
+ control: "text",
+ description: "The suspicious host that was blocked",
+ },
+ },
+ args: {
+ phishingHost: "malicious-example.com",
+ pageIcon: DeactivatedOrg,
+ },
+} satisfies Meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ phishingHost: "malicious-example.com",
+ },
+ decorators: [
+ moduleMetadata({
+ providers: [mockActivatedRoute({ phishingHost: "malicious-example.com" })],
+ }),
+ ],
+};
+
+export const LongHostname: Story = {
+ args: {
+ phishingHost: "very-long-suspicious-phishing-domain-name-that-might-wrap.malicious-example.com",
+ },
+ decorators: [
+ moduleMetadata({
+ providers: [
+ mockActivatedRoute({
+ phishingHost:
+ "very-long-suspicious-phishing-domain-name-that-might-wrap.malicious-example.com",
+ }),
+ ],
+ }),
+ ],
+};
diff --git a/apps/browser/src/dirt/phishing-detection/pages/protected-by-component.html b/apps/browser/src/dirt/phishing-detection/pages/protected-by-component.html
new file mode 100644
index 00000000000..d9f26bc9c90
--- /dev/null
+++ b/apps/browser/src/dirt/phishing-detection/pages/protected-by-component.html
@@ -0,0 +1 @@
+{{ "protectedBy" | i18n: "Bitwarden Phishing Blocker" }}
diff --git a/apps/browser/src/dirt/phishing-detection/pages/learn-more-component.ts b/apps/browser/src/dirt/phishing-detection/pages/protected-by-component.ts
similarity index 63%
rename from apps/browser/src/dirt/phishing-detection/pages/learn-more-component.ts
rename to apps/browser/src/dirt/phishing-detection/pages/protected-by-component.ts
index 1a1e6059204..298c7acd38e 100644
--- a/apps/browser/src/dirt/phishing-detection/pages/learn-more-component.ts
+++ b/apps/browser/src/dirt/phishing-detection/pages/protected-by-component.ts
@@ -4,13 +4,12 @@ import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
import { JslibModule } from "@bitwarden/angular/jslib.module";
-import { ButtonModule } from "@bitwarden/components";
+import { ButtonModule, LinkModule } from "@bitwarden/components";
@Component({
+ selector: "dirt-phishing-protected-by",
standalone: true,
- templateUrl: "learn-more-component.html",
- imports: [CommonModule, CommonModule, JslibModule, ButtonModule],
+ templateUrl: "protected-by-component.html",
+ imports: [CommonModule, CommonModule, JslibModule, ButtonModule, LinkModule],
})
-export class LearnMoreComponent {
- constructor() {}
-}
+export class ProtectedByComponent {}
diff --git a/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.ts b/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.ts
index 54245ae17b4..179431b155c 100644
--- a/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.ts
+++ b/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.ts
@@ -116,15 +116,15 @@ export class PhishingDetectionService {
/**
* Sends a message to the phishing detection service to close the warning page
*/
- static requestClosePhishingWarningPage(): void {
- void chrome.runtime.sendMessage({ command: PhishingDetectionMessage.Close });
+ static async requestClosePhishingWarningPage() {
+ await chrome.runtime.sendMessage({ command: PhishingDetectionMessage.Close });
}
/**
* Sends a message to the phishing detection service to continue to the caught url
*/
static async requestContinueToDangerousUrl() {
- void chrome.runtime.sendMessage({ command: PhishingDetectionMessage.Continue });
+ await chrome.runtime.sendMessage({ command: PhishingDetectionMessage.Continue });
}
/**
diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts
index 17a812f451c..cb5e597e78c 100644
--- a/apps/browser/src/popup/app-routing.module.ts
+++ b/apps/browser/src/popup/app-routing.module.ts
@@ -24,7 +24,6 @@ import {
VaultIcon,
LockIcon,
TwoFactorAuthSecurityKeyIcon,
- DeactivatedOrg,
} from "@bitwarden/assets/svg";
import {
LoginComponent,
@@ -54,8 +53,8 @@ import { BlockedDomainsComponent } from "../autofill/popup/settings/blocked-doma
import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-domains.component";
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component";
import { PremiumV2Component } from "../billing/popup/settings/premium-v2.component";
-import { LearnMoreComponent } from "../dirt/phishing-detection/pages/learn-more-component";
import { PhishingWarning } from "../dirt/phishing-detection/pages/phishing-warning.component";
+import { ProtectedByComponent } from "../dirt/phishing-detection/pages/protected-by-component";
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
import BrowserPopupUtils from "../platform/browser/browser-popup-utils";
import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service";
@@ -718,14 +717,13 @@ const routes: Routes = [
},
{
path: "",
- component: LearnMoreComponent,
+ component: ProtectedByComponent,
outlet: "secondary",
},
],
data: {
- pageIcon: DeactivatedOrg,
- pageTitle: "Bitwarden blocked it!",
- pageSubtitle: "Bitwarden blocked a known phishing site from loading.",
+ hideIcon: true,
+ hideBackgroundIllustration: true,
showReadonlyHostname: true,
} satisfies AnonLayoutWrapperData,
},
diff --git a/apps/browser/src/popup/scss/base.scss b/apps/browser/src/popup/scss/base.scss
index b3d14e65061..01b9d3f05d5 100644
--- a/apps/browser/src/popup/scss/base.scss
+++ b/apps/browser/src/popup/scss/base.scss
@@ -382,7 +382,7 @@ app-root {
}
}
-main:not(popup-page main) {
+main:not(popup-page main):not(auth-anon-layout main) {
position: absolute;
top: 44px;
bottom: 0;
diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts
index ebcd8707597..83535b09e66 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts
+++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts
@@ -302,7 +302,7 @@ export class ItemMoreOptionsComponent {
await this.cipherArchiveService.archiveWithServer(this.cipher.id as CipherId, activeUserId);
this.toastService.showToast({
variant: "success",
- message: this.i18nService.t("itemSentToArchive"),
+ message: this.i18nService.t("itemWasSentToArchive"),
});
}
}
diff --git a/apps/browser/src/vault/popup/settings/archive.component.html b/apps/browser/src/vault/popup/settings/archive.component.html
index 5fb57814fff..faaf0243fc7 100644
--- a/apps/browser/src/vault/popup/settings/archive.component.html
+++ b/apps/browser/src/vault/popup/settings/archive.component.html
@@ -49,7 +49,7 @@
{{ "clone" | i18n }}