mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-03-19 23:28:23 -04:00
Update password field extraction (#541)
This commit is contained in:
@@ -201,7 +201,7 @@ export class FormDetector {
|
||||
// Find primary email field
|
||||
const primaryEmail = this.findInputField(
|
||||
form,
|
||||
['email', 'e-mail', 'mail', 'address', '@'],
|
||||
['email', 'e-mail', 'mail', '@', 'emailaddress'],
|
||||
['text', 'email']
|
||||
);
|
||||
|
||||
@@ -401,9 +401,6 @@ export class FormDetector {
|
||||
return patterns.some(pattern => attributes.some(attr => attr.includes(pattern)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the password field in a form.
|
||||
*/
|
||||
/**
|
||||
* Find the password field in a form.
|
||||
*/
|
||||
@@ -423,7 +420,6 @@ export class FormDetector {
|
||||
const attributes = [
|
||||
input.id,
|
||||
input.name,
|
||||
input.className,
|
||||
input.placeholder
|
||||
].map(attr => attr?.toLowerCase() || '');
|
||||
|
||||
@@ -434,6 +430,11 @@ export class FormDetector {
|
||||
}
|
||||
}
|
||||
|
||||
// If no clear primary password field is found, use the first password field as primary
|
||||
if (!primaryPassword && candidates.length > 0) {
|
||||
primaryPassword = candidates[0];
|
||||
}
|
||||
|
||||
// If we found a primary password, look for a confirmation field
|
||||
if (primaryPassword) {
|
||||
for (const input of Array.from(candidates)) {
|
||||
@@ -454,11 +455,6 @@ export class FormDetector {
|
||||
}
|
||||
}
|
||||
|
||||
// If no clear primary password field is found, use the first password field as primary
|
||||
if (!primaryPassword && candidates.length > 0) {
|
||||
primaryPassword = candidates[0];
|
||||
}
|
||||
|
||||
return {
|
||||
primary: primaryPassword,
|
||||
confirm: confirmPassword
|
||||
|
||||
@@ -64,6 +64,13 @@ describe('FormDetector', () => {
|
||||
testField(FormField.Email, 'field18478', htmlFile);
|
||||
testField(FormField.FirstName, 'field18479', htmlFile);
|
||||
testField(FormField.LastName, 'field18486', htmlFile);
|
||||
});
|
||||
|
||||
describe('Dutch registration form 7 detection', () => {
|
||||
const htmlFile = 'nl-registration-form7.html';
|
||||
|
||||
testField(FormField.Email, 'Form_EmailAddress', htmlFile);
|
||||
testField(FormField.Password, 'Form_Password', htmlFile);
|
||||
testField(FormField.PasswordConfirm, 'Form_RepeatPassword', htmlFile);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
import { FormDetector } from '../FormDetector';
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { JSDOM } from 'jsdom';
|
||||
import { LoginForm } from '../types/LoginForm';
|
||||
|
||||
/**
|
||||
* Load a test HTML file.
|
||||
*/
|
||||
const loadTestHtml = (filename: string): string => {
|
||||
return readFileSync(join(__dirname, 'test-forms', filename), 'utf-8');
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup a form detection test.
|
||||
*/
|
||||
const setupFormTest = (htmlFile: string, focusedElementId?: string) : { document: Document, result: LoginForm } => {
|
||||
const html = loadTestHtml(htmlFile);
|
||||
const dom = new JSDOM(html, {
|
||||
url: 'http://localhost',
|
||||
runScripts: 'dangerously',
|
||||
resources: 'usable'
|
||||
});
|
||||
const document = dom.window.document;
|
||||
|
||||
// Set focus on specified element if provided
|
||||
let focusedElement: HTMLElement | null = null;
|
||||
if (focusedElementId) {
|
||||
focusedElement = document.getElementById(focusedElementId);
|
||||
if (!focusedElement) {
|
||||
throw new Error(`Focus element with id "${focusedElementId}" not found in test HTML`);
|
||||
}
|
||||
focusedElement.focus();
|
||||
|
||||
// Create a new form detector with the focused element.
|
||||
const formDetector = new FormDetector(document, focusedElement);
|
||||
const result = formDetector.detectForms()[0];
|
||||
return { document, result };
|
||||
}
|
||||
|
||||
// No focused element, so just detect the first form.
|
||||
const formDetector = new FormDetector(document);
|
||||
const result = formDetector.detectForms()[0];
|
||||
return { document, result };
|
||||
};
|
||||
|
||||
enum FormField {
|
||||
Username = 'username',
|
||||
FirstName = 'firstName',
|
||||
LastName = 'lastName',
|
||||
Email = 'email',
|
||||
EmailConfirm = 'emailConfirm',
|
||||
Password = 'password',
|
||||
PasswordConfirm = 'passwordConfirm',
|
||||
BirthDate = 'birthdate',
|
||||
BirthDateFormat = 'birthdateFormat',
|
||||
BirthDay = 'birthdateDay',
|
||||
BirthMonth = 'birthdateMonth',
|
||||
BirthYear = 'birthdateYear',
|
||||
Gender = 'gender',
|
||||
GenderMale = 'genderMale',
|
||||
GenderFemale = 'genderFemale',
|
||||
GenderOther = 'genderOther'
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to test field detection
|
||||
*/
|
||||
const testField = (fieldName: FormField, elementId: string, htmlFile: string) : void => {
|
||||
it(`should detect ${fieldName} field`, () => {
|
||||
const { document, result } = setupFormTest(htmlFile, elementId);
|
||||
|
||||
// First verify the test element exists
|
||||
const expectedElement = document.getElementById(elementId);
|
||||
if (!expectedElement) {
|
||||
throw new Error(`Test setup failed: Element with id "${elementId}" not found in test HTML. Check if the element is present in the test HTML file: ${htmlFile}`);
|
||||
}
|
||||
|
||||
// Handle birthdate fields differently
|
||||
if (fieldName === FormField.BirthDate) {
|
||||
expect(result.birthdateField.single).toBe(expectedElement);
|
||||
} else if (fieldName === FormField.BirthDay) {
|
||||
expect(result.birthdateField.day).toBe(expectedElement);
|
||||
} else if (fieldName === FormField.BirthMonth) {
|
||||
expect(result.birthdateField.month).toBe(expectedElement);
|
||||
} else if (fieldName === FormField.BirthYear) {
|
||||
expect(result.birthdateField.year).toBe(expectedElement);
|
||||
}
|
||||
// Handle gender field differently
|
||||
else if (fieldName === FormField.Gender) {
|
||||
expect(result.genderField.field).toBe(expectedElement);
|
||||
} else if (fieldName === FormField.GenderMale) {
|
||||
expect(result.genderField.radioButtons?.male).toBe(expectedElement);
|
||||
} else if (fieldName === FormField.GenderFemale) {
|
||||
expect(result.genderField.radioButtons?.female).toBe(expectedElement);
|
||||
} else if (fieldName === FormField.GenderOther) {
|
||||
expect(result.genderField.radioButtons?.other).toBe(expectedElement);
|
||||
}
|
||||
// Handle default fields
|
||||
else {
|
||||
const fieldKey = `${fieldName}Field` as keyof typeof result;
|
||||
expect(result[fieldKey]).toBeDefined();
|
||||
expect(result[fieldKey]).toBe(expectedElement);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Test the birthdate format.
|
||||
*/
|
||||
const testBirthdateFormat = (expectedFormat: string, htmlFile: string) : void => {
|
||||
it('should detect correct birthdate format', () => {
|
||||
const { result } = setupFormTest(htmlFile);
|
||||
expect(result.birthdateField.format).toBe(expectedFormat);
|
||||
});
|
||||
};
|
||||
|
||||
describe('FormDetector', () => {
|
||||
describe('Dutch registration form detection', () => {
|
||||
const htmlFile = 'nl-registration-form1.html';
|
||||
|
||||
testField(FormField.LastName, 'cpContent_txtAchternaam', htmlFile);
|
||||
testField(FormField.Email, 'cpContent_txtEmail', htmlFile);
|
||||
testField(FormField.Password, 'cpContent_txtWachtwoord', htmlFile);
|
||||
testField(FormField.PasswordConfirm, 'cpContent_txtWachtwoord2', htmlFile);
|
||||
});
|
||||
|
||||
describe('Dutch registration form 2 detection', () => {
|
||||
const htmlFile = 'nl-registration-form2.html';
|
||||
|
||||
testField(FormField.Username, 'register-username', htmlFile);
|
||||
testField(FormField.Email, 'register-email', htmlFile);
|
||||
testField(FormField.Password, 'register-password', htmlFile);
|
||||
|
||||
testField(FormField.BirthDay, 'register-day', htmlFile);
|
||||
testField(FormField.BirthMonth, 'register-month', htmlFile);
|
||||
testField(FormField.BirthYear, 'register-year', htmlFile);
|
||||
|
||||
testField(FormField.GenderMale, 'man', htmlFile);
|
||||
testField(FormField.GenderFemale, 'vrouw', htmlFile);
|
||||
testField(FormField.GenderOther, 'iets', htmlFile);
|
||||
});
|
||||
|
||||
describe('Dutch registration form 3 detection', () => {
|
||||
const htmlFile = 'nl-registration-form3.html';
|
||||
|
||||
testField(FormField.FirstName, 'firstName', htmlFile);
|
||||
testField(FormField.LastName, 'lastName', htmlFile);
|
||||
testField(FormField.Password, 'password', htmlFile);
|
||||
|
||||
testField(FormField.BirthDate, 'date', htmlFile);
|
||||
testBirthdateFormat('dd-mm-yyyy', htmlFile);
|
||||
testField(FormField.GenderMale, 'gender1', htmlFile);
|
||||
testField(FormField.GenderFemale, 'gender2', htmlFile);
|
||||
testField(FormField.GenderOther, 'gender3', htmlFile);
|
||||
});
|
||||
|
||||
describe('Dutch registration form 4 detection', () => {
|
||||
const htmlFile = 'nl-registration-form4.html';
|
||||
|
||||
testField(FormField.Email, 'EmailAddress', htmlFile);
|
||||
});
|
||||
|
||||
describe('Dutch registration form 5 detection', () => {
|
||||
const htmlFile = 'nl-registration-form5.html';
|
||||
|
||||
testField(FormField.Email, 'input_25_5', htmlFile);
|
||||
testField(FormField.Gender, 'input_25_13', htmlFile);
|
||||
testField(FormField.FirstName, 'input_25_14', htmlFile);
|
||||
testField(FormField.LastName, 'input_25_15', htmlFile);
|
||||
testField(FormField.BirthDate, 'input_25_10', htmlFile);
|
||||
testBirthdateFormat('dd/mm/yyyy', htmlFile);
|
||||
});
|
||||
|
||||
describe('English registration form 1 detection', () => {
|
||||
const htmlFile = 'en-registration-form1.html';
|
||||
|
||||
testField(FormField.Email, 'login', htmlFile);
|
||||
testField(FormField.Password, 'password', htmlFile);
|
||||
});
|
||||
|
||||
describe('English registration form 2 detection', () => {
|
||||
const htmlFile = 'en-registration-form2.html';
|
||||
|
||||
testField(FormField.Email, 'signup-email-input', htmlFile);
|
||||
testField(FormField.Username, 'signup-name-input', htmlFile);
|
||||
});
|
||||
|
||||
describe('English registration form 3 detection', () => {
|
||||
const htmlFile = 'en-registration-form3.html';
|
||||
|
||||
testField(FormField.Email, 'email', htmlFile);
|
||||
testField(FormField.EmailConfirm, 'reenter_email', htmlFile);
|
||||
});
|
||||
|
||||
describe('English email form 1 detection', () => {
|
||||
const htmlFile = 'en-email-form1.html';
|
||||
|
||||
testField(FormField.Email, 'P0-0', htmlFile);
|
||||
});
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user