diff --git a/apps/browser-extension/src/utils/formDetector/FieldPatterns.ts b/apps/browser-extension/src/utils/formDetector/FieldPatterns.ts index bc968aab7..111a1332a 100644 --- a/apps/browser-extension/src/utils/formDetector/FieldPatterns.ts +++ b/apps/browser-extension/src/utils/formDetector/FieldPatterns.ts @@ -109,12 +109,15 @@ export const EnglishEmailVerificationPatterns: EmailVerificationPatterns = { /** * English field exclusion patterns. These patterns identify fields that should NOT trigger autofill, - * such as search boxes and filters. These are commonly found in admin panels, - * data tables, and navigation areas where autofill would be inappropriate. + * such as search boxes, filters, and configuration fields. These are commonly found in admin panels, + * data tables, settings pages, and navigation areas where autofill would be inappropriate. */ export const EnglishFieldExclusionPatterns: FieldExclusionPatterns = [ + // Search and filter fields 'search', 'find', 'lookup', 'searchbox', 'search-box', 'searchfield', 'search-field', 'searchinput', 'search-input', 'searchquery', 'search-query', - 'filter', 'filterable', 'filterinput', 'filter-input', 'filterfield', 'filter-field', 'filterbox', 'filter-box' + 'filter', 'filterable', 'filterinput', 'filter-input', 'filterfield', 'filter-field', 'filterbox', 'filter-box', + // Settings and configuration fields + 'setting', 'settings', 'config', 'configuration', 'timeout', 'duration', 'interval', 'refresh', 'access' ]; /** @@ -179,8 +182,11 @@ export const DutchFieldPatterns: FieldPatterns = { * Dutch field exclusion patterns. These patterns identify fields that should NOT trigger autofill. */ export const DutchFieldExclusionPatterns: FieldExclusionPatterns = [ + // Search and filter fields 'zoeken', 'zoek', 'zoekveld', 'zoek-veld', 'zoekinput', 'zoek-input', 'zoekbox', 'zoek-box', - 'filter', 'filteren', 'filterveld', 'filter-veld', 'filterinput', 'filter-input' + 'filter', 'filteren', 'filterveld', 'filter-veld', 'filterinput', 'filter-input', + // Settings and configuration fields + 'instelling', 'instellingen', 'configuratie', 'timeout', 'interval', ]; /** diff --git a/apps/browser-extension/src/utils/formDetector/FormDetector.ts b/apps/browser-extension/src/utils/formDetector/FormDetector.ts index 83cadbb3c..f8e81daf7 100644 --- a/apps/browser-extension/src/utils/formDetector/FormDetector.ts +++ b/apps/browser-extension/src/utils/formDetector/FormDetector.ts @@ -211,7 +211,36 @@ export class FormDetector { } /** - * Check if an input field matches exclusion patterns (search, filter, query fields). + * Check if a pattern matches as a whole word or compound word in the given text. + * Uses word boundaries to avoid false positives. + * + * Examples: + * - "search" matches: "search", "user-search", "searchBox", "search_input" + * - "search" doesn't match: "research", "searchable" (part of another word) + * + * @param text - The text to search in (lowercase). + * @param pattern - The pattern to search for (lowercase). + * @returns True if the pattern matches as a whole word/compound word. + */ + private matchesWordBoundary(text: string, pattern: string): boolean { + /* + * Word boundaries: start of string, space, hyphen, underscore, or transition from lowercase to uppercase + * Pattern must be: + * - At the start: "search", "searchbox", "search-box", "search_box" + * - In the middle: "user-search", "data_search" + * - At the end: "quick-search" + * But NOT within another word: "research" (re-search), "birthdate" (date) + */ + const wordBoundaryPattern = new RegExp( + `(^|[\\s\\-_]|(?<=[a-z])(?=[A-Z]))${pattern}($|[\\s\\-_]|(?<=[a-z])(?=[A-Z]))`, + 'i' + ); + + return wordBoundaryPattern.test(text); + } + + /** + * Check if an input field matches exclusion patterns (search, filter fields). * These fields should not trigger autofill even if they match other patterns. * Uses whole-word matching to avoid false positives (e.g., "date" shouldn't match "birthdate"). * @param input - The input element to check. @@ -246,22 +275,7 @@ export class FormDetector { */ for (const attr of attributesToCheck) { for (const pattern of allExclusionPatterns) { - /* - * Check for whole-word matches or compound word matches (search-box, searchInput, etc.) - * Pattern must be: - * - At the start: "search", "searchbox", "search-box", "search_box" - * - In the middle: "user-search", "data_search" - * - At the end: "quick-search" - * But NOT within another word: "research" (re-search), "birthdate" (date) - * - * Word boundaries: start of string, space, hyphen, underscore, or transition from lowercase to uppercase - */ - const wordBoundaryPattern = new RegExp( - `(^|[\\s\\-_]|(?<=[a-z])(?=[A-Z]))${pattern}($|[\\s\\-_]|(?<=[a-z])(?=[A-Z]))`, - 'i' - ); - - if (wordBoundaryPattern.test(attr)) { + if (this.matchesWordBoundary(attr, pattern)) { return true; } } diff --git a/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.exclusion.test.ts b/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.exclusion.test.ts index c2e2606d4..388423cd0 100644 --- a/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.exclusion.test.ts +++ b/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.exclusion.test.ts @@ -20,6 +20,34 @@ describe('FormDetector - Field Exclusion Patterns', () => { }); }); + describe('Real-world scenario: Settings page with token fields', () => { + const htmlFile = 'exclusion-settings-tokens.html'; + + it('should not trigger TOTP autofill on refresh token lifetime field', () => { + const dom = createTestDom(htmlFile); + const document = dom.window.document; + + const tokenInput = document.getElementById('refreshTokenShort'); + const formDetector = new FormDetector(document, tokenInput as HTMLElement); + + // Should not detect as TOTP form (token fields in settings are not TOTP codes) + expect(formDetector.containsLoginForm()).toBe(false); + expect(formDetector.getDetectedFieldType()).toBeNull(); + }); + + it('should not trigger TOTP autofill on access token lifetime field', () => { + const dom = createTestDom(htmlFile); + const document = dom.window.document; + + const tokenInput = document.getElementById('accessTokenLifetime'); + const formDetector = new FormDetector(document, tokenInput as HTMLElement); + + // Should not detect as TOTP form + expect(formDetector.containsLoginForm()).toBe(false); + expect(formDetector.getDetectedFieldType()).toBeNull(); + }); + }); + describe('Exclusion patterns should not affect legitimate login fields', () => { const htmlFile = 'exclusion-legitimate-login.html'; diff --git a/apps/browser-extension/src/utils/formDetector/__tests__/test-forms/exclusion-settings-tokens.html b/apps/browser-extension/src/utils/formDetector/__tests__/test-forms/exclusion-settings-tokens.html new file mode 100644 index 000000000..7a1dfee58 --- /dev/null +++ b/apps/browser-extension/src/utils/formDetector/__tests__/test-forms/exclusion-settings-tokens.html @@ -0,0 +1,57 @@ + + + + + + Admin Settings - Token Configuration + + + +
+

Authentication Settings

+ + +
+ + +

+ Determines how long the user stays logged in after inactivity. Used when "Remember me" is not checked during login. +

+
+ + +
+ + +

+ JWT token duration for API requests. +

+
+ + +
+ + +
+
+ +