Skip to content

Conversation

@neuronull
Copy link
Contributor

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-28076

📔 Objective

  • Removal of SHIFT from valid modifier keys. As it stands, we allow [SHIFT + ] , which would prevent users from capitalizing letters.
  • As a result, the default shortcut has to change (because it included SHIFT). Changed to CONTROL + ALT + b

📸 Screenshots

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

@neuronull neuronull self-assigned this Nov 12, 2025
@neuronull neuronull added the desktop Desktop Application label Nov 12, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 12, 2025

Logo
Checkmarx One – Scan Summary & Detailsb785498a-b7e8-4c51-b9a6-0d6a019d3e45

New Issues (8)

Checkmarx found the following issues in this Pull Request

Severity Issue Source File / Package Checkmarx Insight
HIGH CVE-2025-11458 Npm-electron-37.7.0
detailsRecommended version: 39.1.0
Description: Heap Buffer Overflow in Sync in Google Chrome prior to 141.0.7390.65 allowed a remote attacker to perform an out-of-bounds memory read via a crafte...
Attack Vector: NETWORK
Attack Complexity: LOW

ID: vT4GchFqHOgM%2BXHyQix4VPNv4wZt%2Bl40D%2BRBmPl4O%2Bk%3D
Vulnerable Package
HIGH CVE-2025-11756 Npm-electron-37.7.0
detailsRecommended version: 39.1.0
Description: Use after free in Safe Browsing in Google Chrome prior to 141.0.7390.107 allowed a remote attacker who had compromised the renderer process to pote...
Attack Vector: NETWORK
Attack Complexity: LOW

ID: sLUlQAOKNoG9ZKZedrCBSHYxkuDtCvDoJETPrWo6TDM%3D
Vulnerable Package
HIGH CVE-2025-12036 Npm-electron-37.7.0
detailsRecommended version: 39.1.0
Description: Out-of-bounds memory access in V8 in Google Chrome prior to 142.0.7444.59 allowed a remote attacker to perform out-of-bounds memory access via a cr...
Attack Vector: NETWORK
Attack Complexity: LOW

ID: dO0CMtQq6KwqD0WAYLLQnb6DMxU2njSDaFtyDdG1JtE%3D
Vulnerable Package
HIGH CVE-2025-12437 Npm-electron-37.7.0
detailsDescription: Use After Free in PageInfo in Google Chrome prior to 142.0.7444.59 allowed a remote attacker who convinced a user to engage in specific UI gestures...
Attack Vector: NETWORK
Attack Complexity: HIGH

ID: Nwsn4lIjLTHS0aP9owpqqyy%2Bi65B%2BdKbLyV6QwzOtd8%3D
Vulnerable Package
MEDIUM CVE-2025-11211 Npm-electron-37.7.0
detailsRecommended version: 39.1.0
Description: Out-of-bounds read in Media in Google Chrome prior to 141.0.7390.54 allowed a remote attacker to potentially perform out-of-bounds memory access vi...
Attack Vector: NETWORK
Attack Complexity: LOW

ID: kj8nGHNniquPec4IvqgwTcbr%2Fgpv1Y2gWeq8WsR2tiw%3D
Vulnerable Package
MEDIUM CVE-2025-12439 Npm-electron-37.7.0
detailsRecommended version: 39.1.0
Description: Inappropriate implementation in App-Bound Encryption in Google Chrome on Windows prior to 142.0.7444.59 allowed a local attacker to obtain potentia...
Attack Vector: LOCAL
Attack Complexity: LOW

ID: uI7xRBfOZf%2FuLYwOWdsZlf%2Fu%2Fy3FBzzU0kpCWsYMwz4%3D
Vulnerable Package
MEDIUM CVE-2025-62522 Npm-vite-6.3.5
detailsRecommended version: 6.4.1
Description: Vite is a frontend tooling framework for JavaScript. In versions 2.9.18 prior to 3.0.0, 3.2.9 prior to 4.0.0, 4.5.3 prior to 5.0.0, 5.2.6 prior to ...
Attack Vector: NETWORK
Attack Complexity: LOW

ID: ShGlBpCTe4KqRqb%2FH6Xlz5nk2D6X22gZyNtId0Pbg9Y%3D
Vulnerable Package
MEDIUM CVE-2025-62522 Npm-vite-6.2.7
detailsRecommended version: 6.4.1
Description: Vite is a frontend tooling framework for JavaScript. In versions 2.9.18 prior to 3.0.0, 3.2.9 prior to 4.0.0, 4.5.3 prior to 5.0.0, 5.2.6 prior to ...
Attack Vector: NETWORK
Attack Complexity: LOW

ID: vCZ6BdfVxz2p1w4bYXRfvV%2BT0%2BoLSsF9IhTQsPWirQY%3D
Vulnerable Package
Fixed Issues (1)

Great job! The following issues were fixed in this Pull Request

Severity Issue Source File / Package
LOW Missing_CSP_Header /bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.html: 11

@codecov
Copy link

codecov bot commented Nov 12, 2025

Codecov Report

❌ Patch coverage is 41.17647% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 40.52%. Comparing base (e7b07d5) to head (f0fd4da).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...autofill/components/autotype-shortcut.component.ts 14.28% 6 Missing ⚠️
...autofill/models/main-autotype-keyboard-shortcut.ts 71.42% 2 Missing ⚠️
.../src/autofill/services/desktop-autotype.service.ts 33.33% 2 Missing ⚠️
Additional details and impacted files
@@                                    Coverage Diff                                    @@
##           autofill/pm-26227/autotype-strict-type-keyboard-input   #17347      +/-   ##
=========================================================================================
+ Coverage                                                  40.51%   40.52%   +0.01%     
=========================================================================================
  Files                                                       3532     3532              
  Lines                                                     101021   101024       +3     
  Branches                                                   15124    15124              
=========================================================================================
+ Hits                                                       40929    40943      +14     
+ Misses                                                     58363    58352      -11     
  Partials                                                    1729     1729              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@neuronull neuronull marked this pull request as ready for review November 12, 2025 17:14
@neuronull neuronull requested a review from a team as a code owner November 12, 2025 17:14
@claude
Copy link

claude bot commented Nov 12, 2025

Claude finished @neuronull's task —— View job


Code Review Complete

  • Analyze previous review comments and author responses
  • Review changes in the latest commits
  • Check test coverage and identify gaps
  • Provide comprehensive review feedback

Summary of Changes Since Last Review

Three commits were added addressing previous feedback:

  • 7f04588: Added Rust test for Shift rejection + improved error handling
  • 5701b04: Removed unnecessary return statements (style cleanup)
  • f0fd4da: Corrected key check from "Super" to "Meta" (DOM API compliance)

Previous Issues - Status Update

Finding 1 (Key name check): ✅ FIXED - Changed back to "Meta" (correct per KeyboardEvent API)
Finding 2 (Duplicate modifiers): ⏳ Deferred - Acknowledged as pre-existing, separate bug ticket recommended
Finding 3 (i18n translations): ⏳ Follow-up - Non-English translations to be handled separately
Finding 4 (Rust Shift test): ✅ FIXED - Test added at line 298-306

Critical Findings

Finding 1: Inconsistent modifier terminology creates confusion

The codebase mixes "Super" (Electron/internal) with "Win" (user-facing) and "Meta" (DOM API):

  • Line 85: hasSuper = event.metaKey (reads Meta from DOM)
  • Line 95: Checks key === "Meta" (correct for DOM)
  • Line 118: Stores as "Super" in array
  • Line 124: Displays as "Win" via .replace("Super", "Win")

Risk: Future maintainers may introduce bugs due to this three-way mapping. The line 124 string replacement is fragile.

Recommended refactoring

Consider normalizing earlier in the pipeline:

if (hasSuper) {
  parts.push("Win"); // Store display name directly
}
// Remove the .replace() call
return parts.join("+");

This eliminates the Super↔Win conversion and reduces cognitive load. The shortcutArray would then match what users see.

Finding 2: Regex validator accepts duplicate modifiers (apps/desktop/src/autofill/components/autotype-shortcut.component.ts:137)

Pattern /^(?=.*\b(Control|Alt|Win)\b)(?:Control\+|Alt\+|Win\+){1,2}[A-Z]$/i matches Control+Control+A.

While physically impossible to trigger, this violates data integrity. Recommended: Add explicit duplicate check or open bug ticket.

Test Coverage Gaps

Codecov reports 41.17% patch coverage with 10 missing lines:

autotype-shortcut.component.ts (6 missing):

  • Lines in buildShortcutFromEvent and onShortcutKeydown lack test coverage
  • No tests verify Shift rejection at UI layer (only validator regex tests exist)

main-autotype-keyboard-shortcut.ts (2 missing):

  • Validation logic branches not fully exercised

desktop-autotype.service.ts (2 missing):

  • Keyboard shortcut configuration paths untested
Recommended test additions
// Test Shift rejection via keyboard event
it("should reject shortcut when shift is pressed", () => {
  const event = new KeyboardEvent("keydown", {
    key: "A",
    ctrlKey: true,
    shiftKey: true, // Should be rejected
  });
  component.onShortcutKeydown(event);
  expect(component.setShortcutForm.controls.shortcut.value).toBe("");
});

// Test Meta key rejection
it("should reject pure Meta key press", () => {
  const event = new KeyboardEvent("keydown", {
    key: "Meta",
    metaKey: true,
  });
  component.onShortcutKeydown(event);
  expect(component.setShortcutForm.controls.shortcut.value).toBe("");
});

Security & Correctness

✅ Shift removal correctly prevents capitalization blocking
✅ Default shortcut changed to Control+Alt+B (safe choice)
✅ Rust layer validates and rejects Shift consistently
✅ Keyboard event handling prevents pure modifier keys

Good Practices Observed

  • Rust test coverage for Shift rejection (type_input.rs:298-306)
  • Improved error message specificity ("Unsupported modifier key: {key}")
  • Consistent validation across TypeScript and Rust layers

Action Items

For this PR:

  1. ⚠️ Consider filing bug ticket for Finding 2 (duplicate modifier validation)
  2. 💭 Optional: Simplify Super→Win terminology (see Finding 1 details)

Follow-up work:
3. Add test coverage for keyboard event Shift rejection
4. Update non-English translation files with new i18n key
5. Test Meta/Win key behavior on actual macOS (verify DOM API returns "Meta" not "Super")


Overall Assessment: The PR successfully removes Shift from valid modifiers and updates the default shortcut. The recent commits addressed key previous feedback. The remaining issues are either acknowledged as future work or lower-priority improvements.

// Allow modifiers in any order, but only 1-2 modifiers total
const pattern =
/^(?=.*\b(Control|Alt|Shift|Win)\b)(?:Control\+|Alt\+|Shift\+|Win\+){1,2}[A-Z]$/i;
const pattern = /^(?=.*\b(Control|Alt|Win)\b)(?:Control\+|Alt\+|Win\+){1,2}[A-Z]$/i;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ This regex pattern will accept duplicate modifiers like Control+Control+A as valid. While this may not cause immediate issues, it's semantically incorrect.

Suggested improvement

Consider adding validation to reject duplicate modifiers:

const pattern = /^(?:(?:Control|Alt|Win)\+){1,2}[A-Z]$/i;
if (!pattern.test(value)) {
  return { invalidShortcut: { message: this.i18nService.t("invalidShortcut") } };
}

// Validate no duplicate modifiers
const parts = value.split('+');
const modifiers = parts.slice(0, -1);
const uniqueModifiers = new Set(modifiers.map(m => m.toLowerCase()));
return uniqueModifiers.size === modifiers.length 
  ? null 
  : { invalidShortcut: { message: this.i18nService.t("invalidShortcut") } };

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't introduce this, it was existing, but it seems legit. If so, I'd vote to open a separate bug

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, it shouldn't be physically possible to do a double modifier key, because the first is already pressed down. but it doesn't hurt to guard against or at least comment on

<div bitDialogContent>
<p>
{{ "editAutotypeShortcutDescription" | i18n }}
{{ "editAutotypeKeyboardModifiersDescription" | i18n }}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ The i18n key changed from editAutotypeShortcutDescription to editAutotypeKeyboardModifiersDescription. Please verify that:

  1. All language translation files (not just en/messages.json) have been updated with this new key
  2. The old key has been removed from all language files to avoid confusion

Otherwise, non-English users may see missing translations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this happens as a follow up in some shape or form

// Allow modifiers in any order, but only 1-2 modifiers total
const pattern =
/^(?=.*\b(Control|Alt|Shift|Win)\b)(?:Control\+|Alt\+|Shift\+|Win\+){1,2}[A-Z]$/i;
const pattern = /^(?=.*\b(Control|Alt|Win)\b)(?:Control\+|Alt\+|Win\+){1,2}[A-Z]$/i;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical: Regex allows duplicate modifiers

While you noted this was pre-existing, the current regex pattern accepts invalid shortcuts like Control+Control+A or Alt+Alt+B. This is semantically incorrect and should be fixed.

Why it happens:
The pattern (?:Control\+|Alt\+|Win\+){1,2} matches any sequence of 1-2 modifier+plus combinations without checking for uniqueness.

Recommended fix:

const pattern = /^(?=.*\b(Control|Alt|Win)\b)(?:Control\+|Alt\+|Win\+){1,2}[A-Z]$/i;
if (!pattern.test(value)) {
  return { invalidShortcut: { message: this.i18nService.t("invalidShortcut") } };
}

// Additional check: ensure no duplicate modifiers
const parts = value.split('+');
const modifiers = parts.slice(0, -1).map(m => m.toLowerCase());
const uniqueModifiers = new Set(modifiers);
if (uniqueModifiers.size !== modifiers.length) {
  return { invalidShortcut: { message: this.i18nService.t("invalidShortcut") } };
}

return null;

While physically triggering duplicate modifiers may be impossible, validating against it prevents edge cases and maintains data integrity.

@neuronull neuronull added the needs-qa Marks a PR as requiring QA approval label Nov 12, 2025
// Allow modifiers in any order, but only 1-2 modifiers total
const pattern =
/^(?=.*\b(Control|Alt|Shift|Win)\b)(?:Control\+|Alt\+|Shift\+|Win\+){1,2}[A-Z]$/i;
const pattern = /^(?=.*\b(Control|Alt|Win)\b)(?:Control\+|Alt\+|Win\+){1,2}[A-Z]$/i;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Validation gap: Regex allows duplicate modifiers

While you noted this is pre-existing and physically impossible to trigger, the regex accepts Control+Control+A or Alt+Alt+B. Consider adding explicit duplicate detection for data integrity:

const pattern = /^(?=.*\b(Control|Alt|Win)\b)(?:Control\+|Alt\+|Win\+){1,2}[A-Z]$/i;
if (!pattern.test(value)) {
  return { invalidShortcut: { message: this.i18nService.t("invalidShortcut") } };
}

// Validate no duplicate modifiers
const parts = value.split('+');
const modifiers = parts.slice(0, -1).map(m => m.toLowerCase());
const uniqueModifiers = new Set(modifiers);
if (uniqueModifiers.size !== modifiers.length) {
  return { invalidShortcut: { message: this.i18nService.t("invalidShortcut") } };
}

return null;

@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

desktop Desktop Application needs-qa Marks a PR as requiring QA approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants