diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml
index b1242ee23ed..5e64dc35875 100644
--- a/.github/workflows/build-browser.yml
+++ b/.github/workflows/build-browser.yml
@@ -149,7 +149,11 @@ jobs:
FILES=$(find . -maxdepth 1 -type f)
for FILE in $FILES; do cp "$FILE" browser-source/; done
- # Copy apps/browser to Browser source directory
+ # Copy patches to the Browser source directory
+ mkdir -p browser-source/patches
+ cp -r patches/* browser-source/patches
+
+ # Copy apps/browser to the Browser source directory
mkdir -p browser-source/apps/browser
cp -r apps/browser/* browser-source/apps/browser
diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml
index e47a2f40f53..7f6d5cc11c4 100644
--- a/.github/workflows/chromatic.yml
+++ b/.github/workflows/chromatic.yml
@@ -37,7 +37,7 @@ jobs:
run: npm run build-storybook:ci
- name: Publish to Chromatic
- uses: chromaui/action@44caff7e88d584b04f79f04e31e819f9a95d4d8f
+ uses: chromaui/action@a45a922b9a7522a4cbb59a7bb7b288a768968924
with:
token: ${{ secrets.GITHUB_TOKEN }}
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml
index 22b76f46408..3dbc08f9859 100644
--- a/.github/workflows/release-desktop.yml
+++ b/.github/workflows/release-desktop.yml
@@ -56,7 +56,7 @@ jobs:
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Branch check
- if: ${{ inputs.release_type != 'Dry Run' }}
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
run: |
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc-desktop" ]]; then
echo "==================================="
@@ -93,7 +93,7 @@ jobs:
esac
- name: Create GitHub deployment
- if: ${{ inputs.release_type != 'Dry Run' }}
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5
id: deployment
with:
@@ -122,7 +122,7 @@ jobs:
cf-prod-account"
- name: Download all artifacts
- if: ${{ inputs.release_type != 'Dry Run' }}
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe
with:
workflow: build-desktop.yml
@@ -131,7 +131,7 @@ jobs:
path: apps/desktop/artifacts
- name: Dry Run - Download all artifacts
- if: ${{ inputs.release_type == 'Dry Run' }}
+ if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe
with:
workflow: build-desktop.yml
@@ -146,7 +146,7 @@ jobs:
run: mv Bitwarden-${{ env.PKG_VERSION }}-universal.pkg Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive
- name: Set staged rollout percentage
- if: ${{ inputs.electron_publish == 'true' }}
+ if: ${{ github.event.inputs.electron_publish == 'true' }}
env:
RELEASE_CHANNEL: ${{ steps.release-channel.outputs.channel }}
ROLLOUT_PCT: ${{ inputs.rollout_percentage }}
@@ -156,7 +156,7 @@ jobs:
echo "stagingPercentage: ${ROLLOUT_PCT}" >> apps/desktop/artifacts/${RELEASE_CHANNEL}-mac.yml
- name: Publish artifacts to S3
- if: ${{ inputs.release_type != 'Dry Run' && inputs.electron_publish == 'true' }}
+ if: ${{ github.event.inputs.release_type != 'Dry Run' && github.event.inputs.electron_publish == 'true' }}
env:
AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-electron-access-id }}
AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-electron-access-key }}
@@ -170,7 +170,7 @@ jobs:
--quiet
- name: Publish artifacts to R2
- if: ${{ inputs.release_type != 'Dry Run' && inputs.electron_publish == 'true' }}
+ if: ${{ github.event.inputs.release_type != 'Dry Run' && github.event.inputs.electron_publish == 'true' }}
env:
AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }}
AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.r2-electron-access-key }}
@@ -192,7 +192,7 @@ jobs:
- name: Create Release
uses: ncipollo/release-action@a2e71bdd4e7dab70ca26a852f29600c98b33153e # v1.12.0
- if: ${{ steps.release-channel.outputs.channel == 'latest' && inputs.release_type != 'Dry Run' && inputs.github_release == 'true' }}
+ if: ${{ steps.release-channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' && github.event.inputs.github_release == 'true' }}
env:
PKG_VERSION: ${{ steps.version.outputs.version }}
RELEASE_CHANNEL: ${{ steps.release-channel.outputs.channel }}
@@ -230,7 +230,7 @@ jobs:
draft: true
- name: Update deployment status to Success
- if: ${{ inputs.release_type != 'Dry Run' && success() }}
+ if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1
with:
token: '${{ secrets.GITHUB_TOKEN }}'
@@ -238,7 +238,7 @@ jobs:
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
- name: Update deployment status to Failure
- if: ${{ inputs.release_type != 'Dry Run' && failure() }}
+ if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1
with:
token: '${{ secrets.GITHUB_TOKEN }}'
@@ -249,7 +249,7 @@ jobs:
name: Deploy Snap
runs-on: ubuntu-22.04
needs: setup
- if: ${{ inputs.snap_publish == 'true' }}
+ if: ${{ github.event.inputs.snap_publish == 'true' }}
env:
_PKG_VERSION: ${{ needs.setup.outputs.release-version }}
steps:
@@ -278,7 +278,7 @@ jobs:
working-directory: apps/desktop
- name: Download Snap artifact
- if: ${{ inputs.release_type != 'Dry Run' }}
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe
with:
workflow: build-desktop.yml
@@ -288,7 +288,7 @@ jobs:
path: apps/desktop/dist
- name: Dry Run - Download Snap artifact
- if: ${{ inputs.release_type == 'Dry Run' }}
+ if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe
with:
workflow: build-desktop.yml
@@ -298,7 +298,7 @@ jobs:
path: apps/desktop/dist
- name: Deploy to Snap Store
- if: ${{ inputs.release_type != 'Dry Run' }}
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }}
run: |
@@ -310,7 +310,7 @@ jobs:
name: Deploy Choco
runs-on: windows-2019
needs: setup
- if: ${{ inputs.choco_publish == 'true' }}
+ if: ${{ github.event.inputs.choco_publish == 'true' }}
env:
_PKG_VERSION: ${{ needs.setup.outputs.release-version }}
steps:
@@ -346,7 +346,7 @@ jobs:
working-directory: apps/desktop
- name: Download choco artifact
- if: ${{ inputs.release_type != 'Dry Run' }}
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe
with:
workflow: build-desktop.yml
@@ -356,7 +356,7 @@ jobs:
path: apps/desktop/dist
- name: Dry Run - Download choco artifact
- if: ${{ inputs.release_type == 'Dry Run' }}
+ if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe
with:
workflow: build-desktop.yml
@@ -366,7 +366,7 @@ jobs:
path: apps/desktop/dist
- name: Push to Chocolatey
- if: ${{ inputs.release_type != 'Dry Run' }}
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
shell: pwsh
run: choco push --source=https://push.chocolatey.org/
working-directory: apps/desktop/dist
diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml
index 563facdb40c..c650e42ecf4 100644
--- a/.github/workflows/version-bump.yml
+++ b/.github/workflows/version-bump.yml
@@ -1,5 +1,6 @@
---
name: Version Bump
+run-name: Version Bump - ${{ github.ref_name }}
on:
workflow_dispatch:
@@ -96,6 +97,26 @@ jobs:
########################
### Browser
+ - name: Browser - Verify input version
+ if: ${{ inputs.bump_browser == true }}
+ env:
+ NEW_VERSION: ${{ inputs.version_number }}
+ run: |
+ CURRENT_VERSION=$(cat package.json | jq -r '.version')
+
+ # Error if version has not changed.
+ if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
+ echo "Version has not changed."
+ exit 1
+ fi
+
+ # Check if version is newer.
+ printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
+ if [ $? -eq 0 ]; then
+ echo "Version check successful."
+ fi
+ working-directory: apps/browser
+
- name: Bump Browser Version
if: ${{ inputs.bump_browser == true }}
env:
@@ -124,6 +145,26 @@ jobs:
prettier --write apps/browser/src/manifest.v3.json
### CLI
+ - name: CLI - Verify input version
+ if: ${{ inputs.bump_cli == true }}
+ env:
+ NEW_VERSION: ${{ inputs.version_number }}
+ run: |
+ CURRENT_VERSION=$(cat package.json | jq -r '.version')
+
+ # Error if version has not changed.
+ if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
+ echo "Version has not changed."
+ exit 1
+ fi
+
+ # Check if version is newer.
+ printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
+ if [ $? -eq 0 ]; then
+ echo "Version check successful."
+ fi
+ working-directory: apps/cli
+
- name: Bump CLI Version
if: ${{ inputs.bump_cli == true }}
env:
@@ -131,6 +172,26 @@ jobs:
run: npm version --workspace=@bitwarden/cli ${VERSION}
### Desktop
+ - name: Desktop - Verify input version
+ if: ${{ inputs.bump_desktop == true }}
+ env:
+ NEW_VERSION: ${{ inputs.version_number }}
+ run: |
+ CURRENT_VERSION=$(cat package.json | jq -r '.version')
+
+ # Error if version has not changed.
+ if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
+ echo "Version has not changed."
+ exit 1
+ fi
+
+ # Check if version is newer.
+ printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
+ if [ $? -eq 0 ]; then
+ echo "Version check successful."
+ fi
+ working-directory: apps/desktop
+
- name: Bump Desktop Version - Root
if: ${{ inputs.bump_desktop == true }}
env:
@@ -145,6 +206,26 @@ jobs:
working-directory: "apps/desktop/src"
### Web
+ - name: Web - Verify input version
+ if: ${{ inputs.bump_web == true }}
+ env:
+ NEW_VERSION: ${{ inputs.version_number }}
+ run: |
+ CURRENT_VERSION=$(cat package.json | jq -r '.version')
+
+ # Error if version has not changed.
+ if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
+ echo "Version has not changed."
+ exit 1
+ fi
+
+ # Check if version is newer.
+ printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
+ if [ $? -eq 0 ]; then
+ echo "Version check successful."
+ fi
+ working-directory: apps/web
+
- name: Bump Web Version
if: ${{ inputs.bump_web == true }}
env:
@@ -176,13 +257,13 @@ jobs:
run: git commit -m "Bumped ${CLIENT} version to ${VERSION}" -a
- name: Push changes
- if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
+ if: ${{ (github.ref == 'refs/heads/master') && (steps.version-changed.outputs.changes_to_commit == 'TRUE') }}
env:
BRANCH: ${{ steps.branch.outputs.branch }}
run: git push -u origin ${BRANCH}
- name: Create Bump Version PR
- if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
+ if: ${{ (github.ref == 'refs/heads/master') && (steps.version-changed.outputs.changes_to_commit == 'TRUE') }}
env:
BASE_BRANCH: master
BRANCH: ${{ steps.branch.outputs.branch }}
diff --git a/apps/browser/package.json b/apps/browser/package.json
index cec3a9caab1..fb7b8ad19bf 100644
--- a/apps/browser/package.json
+++ b/apps/browser/package.json
@@ -1,6 +1,6 @@
{
"name": "@bitwarden/browser",
- "version": "2023.8.3",
+ "version": "2023.9.1",
"scripts": {
"build": "webpack",
"build:mv3": "cross-env MANIFEST_VERSION=3 webpack",
diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json
index 774ca11ecf0..b83fb91390e 100644
--- a/apps/browser/src/_locales/az/messages.json
+++ b/apps/browser/src/_locales/az/messages.json
@@ -1992,7 +1992,7 @@
"message": "Şəxsi anbarın ixracı"
},
"exportingIndividualVaultDescription": {
- "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.",
+ "message": "Yalnız $EMAIL$ ilə əlaqələndirilmiş fərdi anbar elementləri xaricə köçürüləcək. Təşkilat anbar elementləri daxil edilməyəcək. Yalnız anbar element məlumatları xaricə köçürüləcək və əlaqələndirilmiş qoşmalar daxil edilməyəcək.",
"placeholders": {
"email": {
"content": "$1",
diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json
index 08bef1a50c4..6bd4d028b41 100644
--- a/apps/browser/src/_locales/ca/messages.json
+++ b/apps/browser/src/_locales/ca/messages.json
@@ -1992,7 +1992,7 @@
"message": "S'està exportant la caixa forta personal"
},
"exportingIndividualVaultDescription": {
- "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.",
+ "message": "Només s'exportaran els elements de la caixa forta individuals associats a $EMAIL$. Els elements de la caixa de l'organització no s'inclouran. Només s'exportarà la informació de l'element de la caixa forta i no inclourà els fitxers adjunts associats.",
"placeholders": {
"email": {
"content": "$1",
diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json
index fc1c858e7db..a292bf175c3 100644
--- a/apps/browser/src/_locales/el/messages.json
+++ b/apps/browser/src/_locales/el/messages.json
@@ -44,7 +44,7 @@
"message": "Η υπόδειξη του κύριου κωδικού μπορεί να σας βοηθήσει να θυμηθείτε τον κωδικό σας, σε περίπτωση που τον ξεχάσετε."
},
"reTypeMasterPass": {
- "message": "Εισάγετε Ξανά τον Κύριο Κωδικό σας"
+ "message": "Εισάγετε ξανά τον Κύριο Κωδικό"
},
"masterPassHint": {
"message": "Υπόδειξη Κύριου Κωδικού (προαιρετικό)"
@@ -997,7 +997,7 @@
"message": "Μπορείτε να απενεργοποιήσετε την αυτόματη συμπλήρωση φόρτωσης σελίδας για μεμονωμένα στοιχεία σύνδεσης από την προβολή Επεξεργασία στοιχείου."
},
"itemAutoFillOnPageLoad": {
- "message": "Αυτόματη συμπλήρωση της Φόρτισης Σελίδας (αν είναι ενεργοποιημένη στις Επιλογές)"
+ "message": "Αυτόματη συμπλήρωση κατά τη φόρτωση της σελίδας (αν έχει ενεργοποιηθεί στις Ρυθμίσεις)"
},
"autoFillOnPageLoadUseDefault": {
"message": "Χρήση προεπιλεγμένης ρύθμισης"
diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index e074e034412..4b15ef44e8a 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -771,8 +771,8 @@
"featureUnavailable": {
"message": "Feature unavailable"
},
- "updateKey": {
- "message": "You cannot use this feature until you update your encryption key."
+ "encryptionKeyMigrationRequired": {
+ "message": "Encryption key migration required. Please login through the web vault to update your encryption key."
},
"premiumMembership": {
"message": "Premium membership"
diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json
index 697f04154ce..5fafd94ccf8 100644
--- a/apps/browser/src/_locales/hi/messages.json
+++ b/apps/browser/src/_locales/hi/messages.json
@@ -1992,7 +1992,7 @@
"message": "Exporting individual vault"
},
"exportingIndividualVaultDescription": {
- "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.",
+ "message": "केवल $EMAIL$ से संबद्ध, व्यक्तिगत वॉल्ट वस्तुएँ निर्यात की जाएंगी. संगठन वॉल्ट वस्तुएँ शामिल नहीं की जाएंगी. केवल वॉल्ट वस्तुओं की जानकारी निर्यात की जाएगी और इसमें संबंधित अनुलग्नक शामिल नहीं होंगे.",
"placeholders": {
"email": {
"content": "$1",
diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json
index 72d99fcecce..be561aad7c3 100644
--- a/apps/browser/src/_locales/sr/messages.json
+++ b/apps/browser/src/_locales/sr/messages.json
@@ -1992,7 +1992,7 @@
"message": "Извоз личног сефа"
},
"exportingIndividualVaultDescription": {
- "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.",
+ "message": "Само појединачне ставке сефа повезане са $EMAIL$ ће бити извењене. Ставке организационог сефа неће бити укључене. Само информације о ставкама из сефа ће бити извезене и неће укључивати повезане прилоге.",
"placeholders": {
"email": {
"content": "$1",
diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json
index 5325ef6a4f1..64df3ec8778 100644
--- a/apps/browser/src/_locales/zh_CN/messages.json
+++ b/apps/browser/src/_locales/zh_CN/messages.json
@@ -751,7 +751,7 @@
"message": "附件已删除"
},
"newAttachment": {
- "message": "添加新的附件"
+ "message": "添加新附件"
},
"noAttachments": {
"message": "没有附件。"
@@ -853,7 +853,7 @@
"message": "请输入您的验证器应用中的 6 位验证码。"
},
"enterVerificationCodeEmail": {
- "message": "请输入通过电子邮件发送给 $EMAIL$ 的 6 位验证码。",
+ "message": "请输入发送给电子邮件 $EMAIL$ 的 6 位数验证码。",
"placeholders": {
"email": {
"content": "$1",
@@ -1036,7 +1036,7 @@
"message": "值"
},
"newCustomField": {
- "message": "新建自定义字段"
+ "message": "新增自定义字段"
},
"dragToSort": {
"message": "拖动排序"
@@ -1059,7 +1059,7 @@
"description": "This describes a value that is 'linked' (tied) to another value."
},
"popup2faCloseMessage": {
- "message": "如果您点击弹窗外的任何区域,将导致弹窗关闭。您想在新窗口中打开此弹窗,以便它不会关闭吗?"
+ "message": "如果您点击弹窗外的区域以检查您的验证码电子邮件,将导致弹窗关闭。您想在新窗口中打开此弹窗,以便它不会关闭吗?"
},
"popupU2fCloseMessage": {
"message": "此浏览器无法处理此弹出窗口中的 U2F 请求。您想要在新窗口中打开此弹出窗口吗?"
@@ -1182,7 +1182,7 @@
"message": "许可证号码"
},
"email": {
- "message": "Email"
+ "message": "电子邮件"
},
"phone": {
"message": "电话"
@@ -1615,7 +1615,7 @@
"message": "未提供权限"
},
"nativeMessaginPermissionErrorDesc": {
- "message": "没有与 Bitwarden 桌面应用程序通信的权限,我们无法在浏览器扩展中提供生物识别。请再试一次。"
+ "message": "没有与 Bitwarden 桌面应用程序通信的权限,我们无法在浏览器扩展中提供生物识别。请重试。"
},
"nativeMessaginPermissionSidebarTitle": {
"message": "权限请求错误"
@@ -1896,7 +1896,7 @@
"message": "选择文件夹..."
},
"ssoCompleteRegistration": {
- "message": "要完成 SSO 登陆配置,请设置一个主密码以访问和保护您的密码库。"
+ "message": "要完成 SSO 登录配置,请设置一个主密码以访问和保护您的密码库。"
},
"hours": {
"message": "小时"
@@ -1986,13 +1986,13 @@
"message": "字符计数开关"
},
"sessionTimeout": {
- "message": "您的会话已超时。请返回并尝试重新登录。"
+ "message": "您的会话已超时。请返回然后尝试重新登录。"
},
"exportingPersonalVaultTitle": {
"message": "导出个人密码库"
},
"exportingIndividualVaultDescription": {
- "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.",
+ "message": "仅会导出与 $EMAIL$ 关联的个人密码库项目,不包括组织密码库项目。仅会导出密码库项目信息,不包括关联的附件。",
"placeholders": {
"email": {
"content": "$1",
@@ -2117,7 +2117,7 @@
}
},
"loginWithMasterPassword": {
- "message": "使用主密码登录"
+ "message": "主密码登录"
},
"loggingInAs": {
"message": "正登录为"
diff --git a/apps/browser/src/auth/popup/home.component.html b/apps/browser/src/auth/popup/home.component.html
index da208a8d50c..6b42033c4bc 100644
--- a/apps/browser/src/auth/popup/home.component.html
+++ b/apps/browser/src/auth/popup/home.component.html
@@ -9,7 +9,7 @@
-
+
c.reprompt === CipherRepromptType.None);
+ cipher = ciphers[0];
} else {
const ciphers = await this.cipherService.getAllDecrypted();
cipher = ciphers.find((c) => c.id === id);
diff --git a/apps/browser/src/autofill/content/autofill.js b/apps/browser/src/autofill/content/autofill.js
index 1833c09e159..2f3857d3fa8 100644
--- a/apps/browser/src/autofill/content/autofill.js
+++ b/apps/browser/src/autofill/content/autofill.js
@@ -986,13 +986,18 @@
styleTimeout = 200;
/**
- * Fll an element `el` using the value `op` from the fill script
+ * Fill an element `el` using the value `op` from the fill script
* @param {HTMLElement} el
* @param {string} op
*/
function fillTheElement(el, op) {
var shouldCheck;
if (el && null !== op && void 0 !== op && !(el.disabled || el.a || el.readOnly)) {
+ const tabURLChanged = !fillScript.savedUrls?.some(url => url.startsWith(window.location.origin))
+ // Check to make sure the page location didn't change
+ if (tabURLChanged) {
+ return;
+ }
switch (markTheFilling && el.form && !el.form.opfilled && (el.form.opfilled = true),
el.type ? el.type.toLowerCase() : null) {
case 'checkbox':
diff --git a/apps/browser/src/autofill/services/autofill.service.spec.ts b/apps/browser/src/autofill/services/autofill.service.spec.ts
index 5fc4b76c4ca..e6a629cfc9d 100644
--- a/apps/browser/src/autofill/services/autofill.service.spec.ts
+++ b/apps/browser/src/autofill/services/autofill.service.spec.ts
@@ -75,7 +75,7 @@ describe("AutofillService", () => {
const autofillV1Script = "autofill.js";
const autofillV2Script = "autofill-init.js";
const defaultAutofillScripts = ["autofiller.js", "notificationBar.js", "contextMenuHandler.js"];
- const defaultExecuteScriptOptions = { allFrames: true, runAt: "document_start" };
+ const defaultExecuteScriptOptions = { runAt: "document_start" };
let tabMock: chrome.tabs.Tab;
let sender: chrome.runtime.MessageSender;
@@ -91,11 +91,13 @@ describe("AutofillService", () => {
[autofillV1Script, ...defaultAutofillScripts].forEach((scriptName) => {
expect(BrowserApi.executeScriptInTab).toHaveBeenCalledWith(tabMock.id, {
file: `content/${scriptName}`,
+ frameId: sender.frameId,
...defaultExecuteScriptOptions,
});
});
expect(BrowserApi.executeScriptInTab).not.toHaveBeenCalledWith(tabMock.id, {
file: `content/${autofillV2Script}`,
+ frameId: sender.frameId,
...defaultExecuteScriptOptions,
});
});
diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts
index dc2a4918c3b..fcf4dffd4aa 100644
--- a/apps/browser/src/autofill/services/autofill.service.ts
+++ b/apps/browser/src/autofill/services/autofill.service.ts
@@ -62,7 +62,7 @@ export default class AutofillService implements AutofillServiceInterface {
for (const injectedScript of injectedScripts) {
await BrowserApi.executeScriptInTab(sender.tab.id, {
file: `content/${injectedScript}`,
- allFrames: true,
+ frameId: sender.frameId,
runAt: "document_start",
});
}
diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts
index 828d768ca25..0ab74875fbf 100644
--- a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts
+++ b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts
@@ -108,6 +108,7 @@ describe("InsertAutofillContentService", () => {
jest.spyOn(insertAutofillContentService as any, "fillingWithinSandboxedIframe");
jest.spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill");
jest.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill");
+ jest.spyOn(insertAutofillContentService as any, "tabURLChanged");
jest.spyOn(insertAutofillContentService as any, "runFillScriptAction");
insertAutofillContentService.fillForm(fillScript);
@@ -119,6 +120,7 @@ describe("InsertAutofillContentService", () => {
expect(
insertAutofillContentService["userCancelledUntrustedIframeAutofill"]
).not.toHaveBeenCalled();
+ expect(insertAutofillContentService["tabURLChanged"]).not.toHaveBeenCalled();
expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled();
});
@@ -128,6 +130,7 @@ describe("InsertAutofillContentService", () => {
.mockReturnValue(true);
jest.spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill");
jest.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill");
+ jest.spyOn(insertAutofillContentService as any, "tabURLChanged");
jest.spyOn(insertAutofillContentService as any, "runFillScriptAction");
insertAutofillContentService.fillForm(fillScript);
@@ -139,6 +142,7 @@ describe("InsertAutofillContentService", () => {
expect(
insertAutofillContentService["userCancelledUntrustedIframeAutofill"]
).not.toHaveBeenCalled();
+ expect(insertAutofillContentService["tabURLChanged"]).not.toHaveBeenCalled();
expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled();
});
@@ -150,6 +154,7 @@ describe("InsertAutofillContentService", () => {
.spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill")
.mockReturnValue(true);
jest.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill");
+ jest.spyOn(insertAutofillContentService as any, "tabURLChanged");
jest.spyOn(insertAutofillContentService as any, "runFillScriptAction");
insertAutofillContentService.fillForm(fillScript);
@@ -159,6 +164,7 @@ describe("InsertAutofillContentService", () => {
expect(
insertAutofillContentService["userCancelledUntrustedIframeAutofill"]
).not.toHaveBeenCalled();
+ expect(insertAutofillContentService["tabURLChanged"]).not.toHaveBeenCalled();
expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled();
});
@@ -172,6 +178,7 @@ describe("InsertAutofillContentService", () => {
jest
.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill")
.mockReturnValue(true);
+ jest.spyOn(insertAutofillContentService as any, "tabURLChanged").mockReturnValue(false);
jest.spyOn(insertAutofillContentService as any, "runFillScriptAction");
insertAutofillContentService.fillForm(fillScript);
@@ -181,6 +188,31 @@ describe("InsertAutofillContentService", () => {
expect(
insertAutofillContentService["userCancelledUntrustedIframeAutofill"]
).toHaveBeenCalled();
+ expect(insertAutofillContentService["tabURLChanged"]).not.toHaveBeenCalled();
+ expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled();
+ });
+
+ it("returns early if the page location origin does not match against any of the cipher saved URLs", () => {
+ jest
+ .spyOn(insertAutofillContentService as any, "fillingWithinSandboxedIframe")
+ .mockReturnValue(false);
+ jest
+ .spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill")
+ .mockReturnValue(false);
+ jest
+ .spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill")
+ .mockReturnValue(false);
+ jest.spyOn(insertAutofillContentService as any, "tabURLChanged").mockReturnValue(true);
+ jest.spyOn(insertAutofillContentService as any, "runFillScriptAction");
+
+ insertAutofillContentService.fillForm(fillScript);
+
+ expect(insertAutofillContentService["fillingWithinSandboxedIframe"]).toHaveBeenCalled();
+ expect(insertAutofillContentService["userCancelledInsecureUrlAutofill"]).toHaveBeenCalled();
+ expect(
+ insertAutofillContentService["userCancelledUntrustedIframeAutofill"]
+ ).toHaveBeenCalled();
+ expect(insertAutofillContentService["tabURLChanged"]).toHaveBeenCalled();
expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled();
});
@@ -194,6 +226,7 @@ describe("InsertAutofillContentService", () => {
jest
.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill")
.mockReturnValue(false);
+ jest.spyOn(insertAutofillContentService as any, "tabURLChanged").mockReturnValue(false);
jest.spyOn(insertAutofillContentService as any, "runFillScriptAction");
insertAutofillContentService.fillForm(fillScript);
@@ -203,6 +236,7 @@ describe("InsertAutofillContentService", () => {
expect(
insertAutofillContentService["userCancelledUntrustedIframeAutofill"]
).toHaveBeenCalled();
+ expect(insertAutofillContentService["tabURLChanged"]).toHaveBeenCalled();
expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenCalledTimes(3);
expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenNthCalledWith(
1,
diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.ts
index 89f644ba6be..4e47e73704b 100644
--- a/apps/browser/src/autofill/services/insert-autofill-content.service.ts
+++ b/apps/browser/src/autofill/services/insert-autofill-content.service.ts
@@ -38,7 +38,8 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf
!fillScript.script?.length ||
this.fillingWithinSandboxedIframe() ||
this.userCancelledInsecureUrlAutofill(fillScript.savedUrls) ||
- this.userCancelledUntrustedIframeAutofill(fillScript)
+ this.userCancelledUntrustedIframeAutofill(fillScript) ||
+ this.tabURLChanged(fillScript.savedUrls)
) {
return;
}
@@ -46,6 +47,16 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf
fillScript.script.forEach(this.runFillScriptAction);
}
+ /**
+ * Determines if the page URL no longer matches one of the cipher's savedURL domains
+ * @param {string[] | null} savedUrls
+ * @returns {boolean}
+ * @private
+ */
+ private tabURLChanged(savedUrls?: AutofillScript["savedUrls"]): boolean {
+ return savedUrls && !savedUrls.some((url) => url.startsWith(window.location.origin));
+ }
+
/**
* Identifies if the execution of this script is happening
* within a sandboxed iframe.
diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json
index a0a9548c7f3..0f21a10a84f 100644
--- a/apps/browser/src/manifest.json
+++ b/apps/browser/src/manifest.json
@@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
- "version": "2023.8.3",
+ "version": "2023.9.1",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",
diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json
index af8700066f5..b02d7aec847 100644
--- a/apps/browser/src/manifest.v3.json
+++ b/apps/browser/src/manifest.v3.json
@@ -3,7 +3,7 @@
"minimum_chrome_version": "102.0",
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
- "version": "2023.8.3",
+ "version": "2023.9.1",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",
diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts
index d8143859e8f..5ad5ea17317 100644
--- a/apps/browser/src/platform/browser/browser-api.ts
+++ b/apps/browser/src/platform/browser/browser-api.ts
@@ -213,7 +213,10 @@ export class BrowserApi {
chrome.windows.create({ url, focused, type });
}
+ // Keep track of all the events registered in a Safari popup so we can remove
+ // them when the popup gets unloaded, otherwise we cause a memory leak
private static registeredMessageListeners: any[] = [];
+ private static registeredStorageChangeListeners: any[] = [];
static messageListener(
name: string,
@@ -232,21 +235,38 @@ export class BrowserApi {
}
);
- // Keep track of all the events registered in a Safari popup so we can remove
- // them when the popup gets unloaded, otherwise we cause a memory leak
if (BrowserApi.isSafariApi && !BrowserApi.isBackgroundPage(window)) {
BrowserApi.registeredMessageListeners.push(callback);
-
- // The MDN recommend using 'visibilitychange' but that event is fired any time the popup window is obscured as well
- // 'pagehide' works just like 'unload' but is compatible with the back/forward cache, so we prefer using that one
- window.onpagehide = () => {
- for (const callback of BrowserApi.registeredMessageListeners) {
- chrome.runtime.onMessage.removeListener(callback);
- }
- };
+ BrowserApi.setupUnloadListeners();
}
}
+ static storageChangeListener(
+ callback: Parameters
[0]
+ ) {
+ chrome.storage.onChanged.addListener(callback);
+
+ if (BrowserApi.isSafariApi && !BrowserApi.isBackgroundPage(window)) {
+ BrowserApi.registeredStorageChangeListeners.push(callback);
+ BrowserApi.setupUnloadListeners();
+ }
+ }
+
+ // Setup the event to destroy all the listeners when the popup gets unloaded in Safari, otherwise we get a memory leak
+ private static setupUnloadListeners() {
+ // The MDN recommend using 'visibilitychange' but that event is fired any time the popup window is obscured as well
+ // 'pagehide' works just like 'unload' but is compatible with the back/forward cache, so we prefer using that one
+ window.onpagehide = () => {
+ for (const callback of BrowserApi.registeredMessageListeners) {
+ chrome.runtime.onMessage.removeListener(callback);
+ }
+
+ for (const callback of BrowserApi.registeredStorageChangeListeners) {
+ chrome.storage.onChanged.removeListener(callback);
+ }
+ };
+ }
+
static messageListener$() {
return new Observable((subscriber) => {
const handler = (message: unknown) => {
diff --git a/apps/browser/src/platform/services/browser-state.service.ts b/apps/browser/src/platform/services/browser-state.service.ts
index 5e356e7fbe8..ec6851beb8f 100644
--- a/apps/browser/src/platform/services/browser-state.service.ts
+++ b/apps/browser/src/platform/services/browser-state.service.ts
@@ -14,6 +14,7 @@ import { Account } from "../../models/account";
import { BrowserComponentState } from "../../models/browserComponentState";
import { BrowserGroupingsComponentState } from "../../models/browserGroupingsComponentState";
import { BrowserSendComponentState } from "../../models/browserSendComponentState";
+import { BrowserApi } from "../browser/browser-api";
import { browserSession, sessionSync } from "../decorators/session-sync-observable";
import { BrowserStateService as StateServiceAbstraction } from "./abstractions/browser-state.service";
@@ -56,7 +57,7 @@ export class BrowserStateService
// the background page that can get out of sync. We need to work out the
// best way to handle caching with multiple instances of the state service.
if (useAccountCache) {
- chrome.storage.onChanged.addListener((changes, namespace) => {
+ BrowserApi.storageChangeListener((changes, namespace) => {
if (namespace === "local") {
for (const key of Object.keys(changes)) {
if (key !== "accountActivity" && this.accountDiskCache.value[key]) {
diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts
index d5a50687734..b666f55fc9d 100644
--- a/apps/browser/src/popup/app.module.ts
+++ b/apps/browser/src/popup/app.module.ts
@@ -62,7 +62,6 @@ import { PrivateModeWarningComponent } from "./components/private-mode-warning.c
import { SetPinComponent } from "./components/set-pin.component";
import { UserVerificationComponent } from "./components/user-verification.component";
import { ServicesModule } from "./services/services.module";
-import { AboutComponent } from "./settings/about.component";
import { AutofillComponent } from "./settings/autofill.component";
import { ExcludedDomainsComponent } from "./settings/excluded-domains.component";
import { FoldersComponent } from "./settings/folders.component";
@@ -152,7 +151,6 @@ import "../platform/popup/locales";
ViewCustomFieldsComponent,
RemovePasswordComponent,
VaultSelectComponent,
- AboutComponent,
Fido2Component,
HelpAndFeedbackComponent,
AutofillComponent,
diff --git a/apps/browser/src/popup/scss/environment.scss b/apps/browser/src/popup/scss/environment.scss
index 1ac0f4240bd..f6adba86c2e 100644
--- a/apps/browser/src/popup/scss/environment.scss
+++ b/apps/browser/src/popup/scss/environment.scss
@@ -51,15 +51,11 @@ html.browser_safari {
color: $text-muted;
line-height: 25px;
font-weight: 400;
- padding-left: 5px;
+ padding-left: 15px;
- label {
+ span {
font-weight: 600;
- }
-
- a,
- a label:hover {
- cursor: pointer;
+ font-size: $font-size-small;
}
}
@@ -91,7 +87,3 @@ html.browser_safari {
}
}
}
-
-.environment-selector-padding {
- padding-left: 10px;
-}
diff --git a/apps/browser/src/popup/scss/popup.scss b/apps/browser/src/popup/scss/popup.scss
index 7d718b86645..0d7e4281386 100644
--- a/apps/browser/src/popup/scss/popup.scss
+++ b/apps/browser/src/popup/scss/popup.scss
@@ -12,3 +12,4 @@
@import "environment.scss";
@import "pages.scss";
@import "@angular/cdk/overlay-prebuilt.css";
+@import "../../../../../libs/components/src/multi-select/scss/bw.theme";
diff --git a/apps/browser/src/popup/settings/about.component.html b/apps/browser/src/popup/settings/about.component.html
index 24fea4eb9da..b68f592492f 100644
--- a/apps/browser/src/popup/settings/about.component.html
+++ b/apps/browser/src/popup/settings/about.component.html
@@ -1,52 +1,46 @@
-
-
+
+
+
+
diff --git a/apps/browser/src/popup/settings/about.component.ts b/apps/browser/src/popup/settings/about.component.ts
index 459dd7850fa..c0c9012f341 100644
--- a/apps/browser/src/popup/settings/about.component.ts
+++ b/apps/browser/src/popup/settings/about.component.ts
@@ -1,25 +1,29 @@
+import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
import { Observable } from "rxjs";
+import { JslibModule } from "@bitwarden/angular/jslib.module";
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
+import { ButtonModule, DialogModule } from "@bitwarden/components";
import { BrowserApi } from "../../platform/browser/browser-api";
@Component({
- selector: "app-about",
templateUrl: "about.component.html",
+ standalone: true,
+ imports: [CommonModule, JslibModule, DialogModule, ButtonModule],
})
export class AboutComponent {
- serverConfig$: Observable
;
+ protected serverConfig$: Observable = this.configService.serverConfig$;
- year = new Date().getFullYear();
- version = BrowserApi.getApplicationVersion();
- isCloud: boolean;
+ protected year = new Date().getFullYear();
+ protected version = BrowserApi.getApplicationVersion();
+ protected isCloud = this.environmentService.isCloud();
- constructor(configService: ConfigServiceAbstraction, environmentService: EnvironmentService) {
- this.serverConfig$ = configService.serverConfig$;
- this.isCloud = environmentService.isCloud();
- }
+ constructor(
+ private configService: ConfigServiceAbstraction,
+ private environmentService: EnvironmentService
+ ) {}
}
diff --git a/apps/browser/src/popup/settings/settings.component.ts b/apps/browser/src/popup/settings/settings.component.ts
index efeb6aba6d2..c2699ce498e 100644
--- a/apps/browser/src/popup/settings/settings.component.ts
+++ b/apps/browser/src/popup/settings/settings.component.ts
@@ -482,7 +482,7 @@ export class SettingsComponent implements OnInit {
}
about() {
- this.modalService.open(AboutComponent);
+ this.dialogService.open(AboutComponent);
}
async fingerprint() {
diff --git a/apps/browser/src/tools/popup/generator/generator.component.html b/apps/browser/src/tools/popup/generator/generator.component.html
index 5c9c7492014..13d35b6cd7a 100644
--- a/apps/browser/src/tools/popup/generator/generator.component.html
+++ b/apps/browser/src/tools/popup/generator/generator.component.html
@@ -75,7 +75,7 @@
-
+
-
+
-
-
-
-
{{ "updateEncryptionKeyShortDesc" | i18n }}
-
-
-
-
{{ "people" | i18n }}
-
-
-
-
-
+
+
+
-
-
+ [placeholder]="'search' | i18n"
+ >
-