Add browser extension false-positive field exclusion logic (#1935)

This commit is contained in:
Leendert de Borst
2026-04-19 11:14:27 +02:00
committed by Leendert de Borst
parent e6e4dbb6d6
commit 545cce0802
2 changed files with 104 additions and 1 deletions

View File

@@ -44,6 +44,13 @@ export type EmailVerificationPatterns = {
changeEmail: RegExp[];
}
/**
* Type for field exclusion patterns. These patterns are used to exclude fields from autofill detection.
* Fields matching these patterns should not trigger the autofill popup, even if they match
* other field patterns (like username or email).
*/
export type FieldExclusionPatterns = string[];
/**
* English field patterns to detect English form fields.
*/
@@ -100,6 +107,16 @@ 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.
*/
export const EnglishFieldExclusionPatterns: FieldExclusionPatterns = [
'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'
];
/**
* English words to filter out from page titles during autofill matching to
* prevent generic words from causing false positives.
@@ -158,6 +175,14 @@ export const DutchFieldPatterns: FieldPatterns = {
totp: ['verificatiecode', 'eenmalig', 'authenticatie', 'tweefactor', 'beveiligingscode']
};
/**
* Dutch field exclusion patterns. These patterns identify fields that should NOT trigger autofill.
*/
export const DutchFieldExclusionPatterns: FieldExclusionPatterns = [
'zoeken', 'zoek', 'zoekveld', 'zoek-veld', 'zoekinput', 'zoek-input', 'zoekbox', 'zoek-box',
'filter', 'filteren', 'filterveld', 'filter-veld', 'filterinput', 'filter-input'
];
/**
* Dutch gender option patterns
*/
@@ -314,3 +339,13 @@ export const CombinedEmailVerificationPatterns: EmailVerificationPatterns = {
...DutchEmailVerificationPatterns.changeEmail
]
};
/**
* Combined field exclusion patterns from all supported languages. These patterns identify fields
* that should NOT trigger autofill, regardless of whether they match other field patterns.
* This prevents false positives on search boxes and filters commonly found
* in admin panels, data tables, and navigation areas.
*/
export const CombinedFieldExclusionPatterns: FieldExclusionPatterns = [
...new Set([...EnglishFieldExclusionPatterns, ...DutchFieldExclusionPatterns])
];

View File

@@ -1,4 +1,4 @@
import { CombinedEmailVerificationPatterns, CombinedFieldPatterns, CombinedGenderOptionPatterns, CombinedStopWords } from "./FieldPatterns";
import { CombinedEmailVerificationPatterns, CombinedFieldExclusionPatterns, CombinedFieldPatterns, CombinedGenderOptionPatterns, CombinedStopWords } from "./FieldPatterns";
import { DetectedFieldType, FormFields } from "./types/FormFields";
/**
@@ -210,6 +210,66 @@ export class FormDetector {
return wrapper;
}
/**
* Check if an input field matches exclusion patterns (search, filter, query 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.
* @returns True if the field matches exclusion patterns and should be excluded from autofill.
*/
private matchesExclusionPatterns(input: HTMLInputElement): boolean {
// Collect all text attributes to check
const attributesToCheck = [
input.id,
input.getAttribute('name'),
input.getAttribute('placeholder'),
input.getAttribute('class'),
input.getAttribute('aria-label')
]
.map(a => a?.toLowerCase() ?? '')
.filter(a => a.length > 0);
// Also check associated labels
if (input.id || input.getAttribute('name')) {
const label = this.document.querySelector(`label[for="${input.id || input.getAttribute('name')}"]`);
if (label) {
attributesToCheck.push(label.textContent?.toLowerCase() ?? '');
}
}
// Use the combined exclusion patterns
const allExclusionPatterns = CombinedFieldExclusionPatterns;
/*
* Check if any attribute contains any exclusion pattern
* Use whole-word or compound-word matching to avoid false positives
*/
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)) {
return true;
}
}
}
return false;
}
/**
* Check if an input field is likely a fake/honeypot field used to prevent autofill.
* These fields are intentionally hidden from users but present in the DOM.
@@ -543,6 +603,14 @@ export class FormDetector {
continue;
}
/*
* Skip fields that match exclusion patterns (search, filter, query fields).
* These should never trigger autofill, even if they match other patterns.
*/
if (this.matchesExclusionPatterns(input as HTMLInputElement)) {
continue;
}
/*
* Skip fake/honeypot fields (e.g., fields with "fake" in name/id, tabindex="-1", etc.)
*/