1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 14:23:32 +00:00

autofill improvements for cards and identities

This commit is contained in:
Kyle Spearrin
2017-11-10 23:47:42 -05:00
parent 8b0019ea94
commit 12851a6e80

View File

@@ -9,9 +9,9 @@ import TokenService from './token.service';
import TotpService from './totp.service'; import TotpService from './totp.service';
import UtilsService from './utils.service'; import UtilsService from './utils.service';
const CardAttributes: string[] = ['autoCompleteType', 'data-stripe', 'htmlName', 'htmlID']; const CardAttributes: string[] = ['autoCompleteType', 'data-stripe', 'htmlName', 'htmlID', 'label-tag'];
const IdentityAttributes: string[] = ['autoCompleteType', 'data-stripe', 'htmlName', 'htmlID']; const IdentityAttributes: string[] = ['autoCompleteType', 'data-stripe', 'htmlName', 'htmlID', 'label-tag'];
const UsernameFieldNames: string[] = ['username', 'user name', 'email', 'email address', 'e-mail', 'e-mail address', const UsernameFieldNames: string[] = ['username', 'user name', 'email', 'email address', 'e-mail', 'e-mail address',
'userid', 'user id']; 'userid', 'user id'];
@@ -86,7 +86,7 @@ var IsoProvinces: { [id: string]: string; } = {
export default class AutofillService { export default class AutofillService {
constructor(public cipherService: CipherService, public tokenService: TokenService, constructor(public cipherService: CipherService, public tokenService: TokenService,
public totpService: TotpService, public utilsService: UtilsService) { public totpService: TotpService, public utilsService: UtilsService) {
} }
getFormsWithPasswordFields(pageDetails: AutofillPageDetails): any[] { getFormsWithPasswordFields(pageDetails: AutofillPageDetails): any[] {
@@ -286,8 +286,8 @@ export default class AutofillService {
} }
private generateLoginFillScript(fillScript: AutofillScript, pageDetails: any, private generateLoginFillScript(fillScript: AutofillScript, pageDetails: any,
filledFields: { [id: string]: AutofillField; }, filledFields: { [id: string]: AutofillField; },
options: any): AutofillScript { options: any): AutofillScript {
if (!options.cipher.login) { if (!options.cipher.login) {
return null; return null;
} }
@@ -397,8 +397,8 @@ export default class AutofillService {
} }
private generateCardFillScript(fillScript: AutofillScript, pageDetails: any, private generateCardFillScript(fillScript: AutofillScript, pageDetails: any,
filledFields: { [id: string]: AutofillField; }, filledFields: { [id: string]: AutofillField; },
options: any): AutofillScript { options: any): AutofillScript {
if (!options.cipher.card) { if (!options.cipher.card) {
return null; return null;
} }
@@ -410,49 +410,36 @@ export default class AutofillService {
if (f.hasOwnProperty(attr) && f[attr]) { if (f.hasOwnProperty(attr) && f[attr]) {
// ref https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill // ref https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill
// ref https://developers.google.com/web/fundamentals/design-and-ux/input/forms/ // ref https://developers.google.com/web/fundamentals/design-and-ux/input/forms/
switch (f[attr].toLowerCase()) { if (!fillFields.cardholderName && this.isFieldMatch(f[attr],
case 'cc-name': case 'ccname': case 'cardname': case 'card-name': case 'cardholder': ['cc-name', 'card-name', 'cardholder-name', 'cardholder', 'name'],
case 'cardholdername': case 'cardholder-name': case 'name': ['cc-name', 'card-name', 'cardholder-name', 'cardholder'])) {
if (!fillFields.cardholderName) { fillFields.cardholderName = f;
fillFields.cardholderName = f; } else if (!fillFields.number && this.isFieldMatch(f[attr],
} ['cc-number', 'cc-num', 'card-number', 'card-num', 'number'],
break; ['cc-number', 'cc-num', 'card-number', 'card-num'])) {
case 'cc-number': case 'ccnumber': case 'cardnumber': case 'card-number': case 'number': fillFields.number = f;
if (!fillFields.number) { } else if (!fillFields.exp && this.isFieldMatch(f[attr],
fillFields.number = f; ['cc-exp', 'card-exp', 'cc-expiration', 'card-expiration', 'cc-ex', 'card-ex'],
} [])) {
break; fillFields.exp = f;
case 'cc-exp': case 'ccexp': case 'cardexp': case 'card-exp': case 'cc-expiration': } else if (!fillFields.expMonth && this.isFieldMatch(f[attr],
case 'ccexpiration': case 'card-expiration': case 'cardexpiration': ['exp-month', 'cc-exp-month', 'cc-month', 'card-month', 'cc-mo', 'card-mo', 'exp-mo',
if (!fillFields.exp) { 'card-exp-mo', 'cc-exp-mo', 'card-expiration-month', 'expiration-month',
fillFields.exp = f; 'cc-mm', 'card-mm', 'card-exp-mm', 'cc-exp-mm', 'exp-mm'])) {
} fillFields.expMonth = f;
break; } else if (!fillFields.expYear && this.isFieldMatch(f[attr],
case 'exp-month': case 'expmonth': case 'ccexpmonth': case 'cc-exp-month': case 'cc-month': ['exp-year', 'cc-exp-year', 'cc-year', 'card-year', 'cc-yr', 'card-yr', 'exp-yr',
case 'ccmonth': case 'card-month': case 'cardmonth': 'card-exp-yr', 'cc-exp-yr', 'card-expiration-year', 'expiration-year',
if (!fillFields.expMonth) { 'cc-yy', 'card-yy', 'card-exp-yy', 'cc-exp-yy', 'exp-yy',
fillFields.expMonth = f; 'cc-yyyy', 'card-yyyy', 'card-exp-yyyy', 'cc-exp-yyyy'])) {
} fillFields.expYear = f;
break; } else if (!fillFields.code && this.isFieldMatch(f[attr],
case 'exp-year': case 'expyear': case 'ccexpyear': case 'cc-exp-year': case 'cc-year': ['cvv', 'cvc', 'cvv2', 'cc-csc', 'cc-cvv', 'card-csc', 'card-cvv', 'cvd',
case 'ccyear': case 'card-year': case 'cardyear': 'cid', 'cvc2', 'cnv', 'cvn2', 'cc-code', 'card-code'])) {
if (!fillFields.expYear) { fillFields.code = f;
fillFields.expYear = f; } else if (!fillFields.brand && this.isFieldMatch(f[attr],
} ['cc-type', 'card-type', 'card-brand', 'cc-brand'])) {
break; fillFields.brand = f;
case 'cvc': case 'cvv': case 'cvv2': case 'cc-csc': case 'cc-cvv': case 'card-csc':
case 'cardcsc': case 'cvd': case 'cid': case 'cvc2': case 'cvn': case 'cvn2':
if (!fillFields.code) {
fillFields.code = f;
}
break;
case 'card-type': case 'cc-type': case 'cardtype': case 'cctype':
if (!fillFields.brand) {
fillFields.brand = f;
}
break;
default:
break;
} }
} }
} }
@@ -482,8 +469,8 @@ export default class AutofillService {
} }
private generateIdentityFillScript(fillScript: AutofillScript, pageDetails: any, private generateIdentityFillScript(fillScript: AutofillScript, pageDetails: any,
filledFields: { [id: string]: AutofillField; }, filledFields: { [id: string]: AutofillField; },
options: any): AutofillScript { options: any): AutofillScript {
if (!options.cipher.identity) { if (!options.cipher.identity) {
return null; return null;
} }
@@ -495,113 +482,60 @@ export default class AutofillService {
if (f.hasOwnProperty(attr) && f[attr]) { if (f.hasOwnProperty(attr) && f[attr]) {
// ref https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill // ref https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill
// ref https://developers.google.com/web/fundamentals/design-and-ux/input/forms/ // ref https://developers.google.com/web/fundamentals/design-and-ux/input/forms/
switch (f[attr].toLowerCase()) { if (!fillFields.name && this.isFieldMatch(f[attr],
case 'name': case 'full-name': case 'fullname': case 'your-name': case 'yourname': ['name', 'full-name', 'your-name'], ['full-name', 'your-name'])) {
case 'full_name': case 'your_name': fillFields.name = f;
if (!fillFields.name) { } else if (!fillFields.firstName && this.isFieldMatch(f[attr],
fillFields.name = f; ['f-name', 'first-name', 'given-name', 'first-n'])) {
} fillFields.firstName = f;
break; } else if (!fillFields.middleName && this.isFieldMatch(f[attr],
case 'fname': case 'firstname': case 'first-name': case 'given-name': case 'givenname': ['m-name', 'middle-name', 'additional-name', 'middle-initial', 'middle-n', 'middle-i'])) {
case 'first_name': case 'given_name': fillFields.middleName = f;
if (!fillFields.firstName) { } else if (!fillFields.lastName && this.isFieldMatch(f[attr],
fillFields.firstName = f; ['l-name', 'last-name', 's-name', 'surname', 'family-name', 'family-n', 'last-n'])) {
} fillFields.lastName = f;
break; } else if (!fillFields.title && this.isFieldMatch(f[attr],
case 'mname': case 'middlename': case 'middle-name': case 'additional-name': ['honorific-prefix', 'prefix', 'title'])) {
case 'additionalname': case 'middle_name': case 'additional_name': fillFields.title = f;
if (!fillFields.middleName) { } else if (!fillFields.email && this.isFieldMatch(f[attr],
fillFields.middleName = f; ['e-mail', 'email-address'])) {
} fillFields.email = f;
break; } else if (!fillFields.address && this.isFieldMatch(f[attr],
case 'lname': case 'lastname': case 'last-name': case 'family-name': case 'familyname': ['address', 'street-address'], [])) {
case 'surname': case 'sname': case 'last_name': case 'family_name': fillFields.address = f;
if (!fillFields.lastName) { } else if (!fillFields.address1 && this.isFieldMatch(f[attr],
fillFields.lastName = f; ['address-1', 'address-line-1'])) {
} fillFields.address1 = f;
break; } else if (!fillFields.address2 && this.isFieldMatch(f[attr],
case 'honorific-prefix': case 'prefix': case 'honorific_prefix': ['address-2', 'address-line-2'])) {
if (!fillFields.title) { fillFields.address2 = f;
fillFields.title = f; } else if (!fillFields.address3 && this.isFieldMatch(f[attr],
} ['address-3', 'address-line-3'])) {
break; fillFields.address3 = f;
case 'email': case 'e-mail': case 'email-address': case 'emailaddress': case 'email_address': } else if (!fillFields.city && this.isFieldMatch(f[attr],
if (!fillFields.email) { ['city', 'town', 'address-level-2', 'address-city', 'address-town'])) {
fillFields.email = f; fillFields.city = f;
} } else if (!fillFields.state && this.isFieldMatch(f[attr],
break; ['state', 'province', 'provence', 'address-level-1', 'address-state',
case 'address': case 'street_address': case 'street-address': case 'streetaddress': 'address-province'])) {
if (!fillFields.address) { fillFields.state = f;
fillFields.address = f; } else if (!fillFields.postalCode && this.isFieldMatch(f[attr],
} ['postal', 'zip', 'zip2', 'zip-code', 'postal-code', 'address-zip', 'address-postal',
break; 'address-code', 'address-postal-code', 'address-zip-code'])) {
case 'address1': case 'address-1': case 'address-line1': case 'address_1': fillFields.postalCode = f;
case 'address_line1': } else if (!fillFields.country && this.isFieldMatch(f[attr],
if (!fillFields.address1) { ['country', 'country-code', 'country-name', 'address-country', 'address-country-name',
fillFields.address1 = f; 'address-country-code'])) {
} fillFields.country = f;
break; } else if (!fillFields.phone && this.isFieldMatch(f[attr],
case 'address2': case 'address-2': case 'address-line2': case 'address_2': ['phone', 'mobile', 'mobile-phone', 'tel', 'telephone', 'phone-number'])) {
case 'address_line2': fillFields.phone = f;
if (!fillFields.address2) { } else if (!fillFields.username && this.isFieldMatch(f[attr],
fillFields.address2 = f; ['user-name', 'user-id', 'screen-name'])) {
} fillFields.username = f;
break; } else if (!fillFields.company && this.isFieldMatch(f[attr],
case 'address3': case 'address-3': case 'address-line3': case 'address_3': ['company', 'company-name', 'organization', 'organization-name'])) {
case 'address_line3': fillFields.company = f;
if (!fillFields.address3) {
fillFields.address3 = f;
}
break;
case 'city': case 'town': case 'address-level2': case 'address_level2': case 'address_city':
case 'address_town': case 'address-city':
if (!fillFields.city) {
fillFields.city = f;
}
break;
case 'state': case 'province': case 'provence': case 'address-level1': case 'address_level1':
case 'address_state': case 'address_province': case 'address-state': case 'address-province':
if (!fillFields.state) {
fillFields.state = f;
}
break;
case 'postal': case 'postal-code': case 'zip': case 'zip2': case 'zip-code':
case 'zipcode': case 'postalcode': case 'postal_code': case 'zip_code':
case 'address_zip': case 'address_postal': case 'address-postal-code':
case 'address_postal_code': case 'address_code': case 'address_postalcode':
case 'address_zip_code':
if (!fillFields.postalCode) {
fillFields.postalCode = f;
}
break;
case 'country': case 'country-code': case 'countrycode': case 'countryname':
case 'country-name': case 'country_name': case 'country_code': case 'address_country':
case 'address-country': case 'address-countryname': case 'address-countrycode':
case 'address_countryname': case 'address_countrycode':
if (!fillFields.country) {
fillFields.country = f;
}
break;
case 'phone': case 'mobile': case 'mobile-phone': case 'tel': case 'telephone':
case 'mobile_phone':
if (!fillFields.phone) {
fillFields.phone = f;
}
break;
case 'username': case 'user-name': case 'userid': case 'user-id': case 'user_name':
case 'user_id':
if (!fillFields.username) {
fillFields.username = f;
}
break;
case 'company': case 'organization': case 'organisation': case 'company_name':
case 'organization_name':
if (!fillFields.company) {
fillFields.company = f;
}
break;
default:
break;
} }
} }
} }
@@ -703,9 +637,22 @@ export default class AutofillService {
return fillScript; return fillScript;
} }
private isFieldMatch(value: string, options: string[], containsOptions?: string[]): boolean {
value = value.trim().toLowerCase().replace(/-|_| /g, '');
for (let option of options) {
const checkValueContains = containsOptions == null || containsOptions.indexOf(option) > -1;
option = option.replace(/-/g, '');
if (value === option || (checkValueContains && value.indexOf(option) > -1)) {
return true;
}
}
return false;
}
private makeScriptAction(fillScript: AutofillScript, cipherData: any, fillFields: any, private makeScriptAction(fillScript: AutofillScript, cipherData: any, fillFields: any,
filledFields: { [id: string]: AutofillField; }, dataProp: string, filledFields: { [id: string]: AutofillField; }, dataProp: string,
fieldProp?: string) { fieldProp?: string) {
fieldProp = fieldProp || dataProp; fieldProp = fieldProp || dataProp;
if (cipherData[dataProp] && cipherData[dataProp] !== '' && fillFields[fieldProp]) { if (cipherData[dataProp] && cipherData[dataProp] !== '' && fillFields[fieldProp]) {
filledFields[fillFields[fieldProp].opid] = fillFields[fieldProp]; filledFields[fillFields[fieldProp].opid] = fillFields[fieldProp];
@@ -726,7 +673,7 @@ export default class AutofillService {
} }
private findUsernameField(pageDetails: AutofillPageDetails, passwordField: AutofillField, canBeHidden: boolean, private findUsernameField(pageDetails: AutofillPageDetails, passwordField: AutofillField, canBeHidden: boolean,
withoutForm: boolean) { withoutForm: boolean) {
let usernameField: AutofillField = null; let usernameField: AutofillField = null;
for (const f of pageDetails.fields) { for (const f of pageDetails.fields) {
if (f.elementNumber >= passwordField.elementNumber) { if (f.elementNumber >= passwordField.elementNumber) {
@@ -799,7 +746,7 @@ export default class AutofillService {
} }
private setFillScriptForFocus(filledFields: { [id: string]: AutofillField; }, private setFillScriptForFocus(filledFields: { [id: string]: AutofillField; },
fillScript: AutofillScript): AutofillScript { fillScript: AutofillScript): AutofillScript {
let lastField: AutofillField = null; let lastField: AutofillField = null;
let lastPasswordField: AutofillField = null; let lastPasswordField: AutofillField = null;