Store users' confirmation to fill foreign form in settings (#35)
This commit is contained in:
@@ -10,7 +10,8 @@ var appID = "com.github.browserpass.native";
|
|||||||
// default settings
|
// default settings
|
||||||
var defaultSettings = {
|
var defaultSettings = {
|
||||||
gpgPath: null,
|
gpgPath: null,
|
||||||
stores: {}
|
stores: {},
|
||||||
|
foreignFills: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
var authListeners = {};
|
var authListeners = {};
|
||||||
@@ -92,7 +93,7 @@ function saveRecent(settings, login, remove = false) {
|
|||||||
/**
|
/**
|
||||||
* Call injected form-fill code
|
* Call injected form-fill code
|
||||||
*
|
*
|
||||||
* @param object tab Target tab
|
* @param object settings Settings object
|
||||||
* @param object fillRequest Fill request details
|
* @param object fillRequest Fill request details
|
||||||
* @param boolean allFrames Dispatch to all frames
|
* @param boolean allFrames Dispatch to all frames
|
||||||
* @param boolean allowForeign Allow foreign-origin iframes
|
* @param boolean allowForeign Allow foreign-origin iframes
|
||||||
@@ -100,7 +101,7 @@ function saveRecent(settings, login, remove = false) {
|
|||||||
* @return array list of filled fields
|
* @return array list of filled fields
|
||||||
*/
|
*/
|
||||||
async function dispatchFill(
|
async function dispatchFill(
|
||||||
tab,
|
settings,
|
||||||
fillRequest,
|
fillRequest,
|
||||||
allFrames = false,
|
allFrames = false,
|
||||||
allowForeign = false,
|
allowForeign = false,
|
||||||
@@ -108,36 +109,50 @@ async function dispatchFill(
|
|||||||
) {
|
) {
|
||||||
fillRequest = Object.assign(deepCopy(fillRequest), {
|
fillRequest = Object.assign(deepCopy(fillRequest), {
|
||||||
allowForeign: allowForeign,
|
allowForeign: allowForeign,
|
||||||
allowNoSecret: allowNoSecret
|
allowNoSecret: allowNoSecret,
|
||||||
|
approvedForeign: settings.foreignFills[settings.host]
|
||||||
});
|
});
|
||||||
|
|
||||||
var filledFields = await chrome.tabs.executeScript(tab.id, {
|
var perFrameFillResults = await chrome.tabs.executeScript(settings.tab.id, {
|
||||||
allFrames: allFrames,
|
allFrames: allFrames,
|
||||||
code: `window.browserpass.fillLogin(${JSON.stringify(fillRequest)});`
|
code: `window.browserpass.fillLogin(${JSON.stringify(fillRequest)});`
|
||||||
});
|
});
|
||||||
|
|
||||||
// simplify the list of filled fields
|
// merge fill resutls in a single object
|
||||||
filledFields = filledFields
|
var fillResult = perFrameFillResults.reduce(
|
||||||
.reduce((fields, addFields) => fields.concat(addFields), [])
|
function(merged, frameResult) {
|
||||||
.reduce(function(fields, field) {
|
if (typeof frameResult.foreignFill !== "undefined") {
|
||||||
if (!fields.includes(field)) {
|
merged.foreignFill = frameResult.foreignFill;
|
||||||
fields.push(field);
|
|
||||||
}
|
}
|
||||||
return fields;
|
for (var field in frameResult.filledFields) {
|
||||||
}, []);
|
if (!merged.filledFields.includes(field)) {
|
||||||
|
merged.filledFields.push(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return merged;
|
||||||
|
},
|
||||||
|
{ filledFields: [] }
|
||||||
|
);
|
||||||
|
|
||||||
return filledFields;
|
// if user answered a foreign-origin confirmation,
|
||||||
|
// store the answer in the settings
|
||||||
|
if (typeof fillResult.foreignFill !== "undefined") {
|
||||||
|
settings.foreignFills[settings.host] = fillResult.foreignFill;
|
||||||
|
saveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fillResult.filledFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill form fields
|
* Fill form fields
|
||||||
*
|
*
|
||||||
* @param object tab Target tab
|
* @param object settings Settings object
|
||||||
* @param object login Login object
|
* @param object login Login object
|
||||||
* @param array fields List of fields to fill
|
* @param array fields List of fields to fill
|
||||||
* @return array List of filled fields
|
* @return array List of filled fields
|
||||||
*/
|
*/
|
||||||
async function fillFields(tab, login, fields) {
|
async function fillFields(settings, login, fields) {
|
||||||
// check that required fields are present
|
// check that required fields are present
|
||||||
for (var field of fields) {
|
for (var field of fields) {
|
||||||
if (login.fields[field] === null) {
|
if (login.fields[field] === null) {
|
||||||
@@ -146,39 +161,43 @@ async function fillFields(tab, login, fields) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// inject script
|
// inject script
|
||||||
await chrome.tabs.executeScript(tab.id, {
|
await chrome.tabs.executeScript(settings.tab.id, {
|
||||||
allFrames: true,
|
allFrames: true,
|
||||||
file: "js/inject.dist.js"
|
file: "js/inject.dist.js"
|
||||||
});
|
});
|
||||||
|
|
||||||
// build fill request
|
// build fill request
|
||||||
var fillRequest = {
|
var fillRequest = {
|
||||||
origin: new URL(tab.url).origin,
|
origin: new URL(settings.tab.url).origin,
|
||||||
login: login,
|
login: login,
|
||||||
fields: fields
|
fields: fields
|
||||||
};
|
};
|
||||||
|
|
||||||
// fill form via injected script
|
// fill form via injected script
|
||||||
var filledFields = await dispatchFill(tab, fillRequest);
|
var filledFields = await dispatchFill(settings, fillRequest);
|
||||||
|
|
||||||
// try again using same-origin frames if we couldn't fill a password field
|
// try again using same-origin frames if we couldn't fill a password field
|
||||||
if (!filledFields.includes("secret")) {
|
if (!filledFields.includes("secret")) {
|
||||||
filledFields = filledFields.concat(await dispatchFill(tab, fillRequest, true));
|
filledFields = filledFields.concat(await dispatchFill(settings, fillRequest, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// try again using all available frames if we couldn't fill a password field
|
// try again using all available frames if we couldn't fill a password field
|
||||||
if (!filledFields.includes("secret")) {
|
if (!filledFields.includes("secret") && settings.foreignFills[settings.host] !== false) {
|
||||||
filledFields = filledFields.concat(await dispatchFill(tab, fillRequest, true, true));
|
filledFields = filledFields.concat(await dispatchFill(settings, fillRequest, true, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// try again using same-origin frames, and don't require a password field
|
// try again using same-origin frames, and don't require a password field
|
||||||
if (!filledFields.length) {
|
if (!filledFields.length) {
|
||||||
filledFields = filledFields.concat(await dispatchFill(tab, fillRequest, true, false, true));
|
filledFields = filledFields.concat(
|
||||||
|
await dispatchFill(settings, fillRequest, true, false, true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// try again using all available frames, and don't require a password field
|
// try again using all available frames, and don't require a password field
|
||||||
if (!filledFields.length) {
|
if (!filledFields.length && settings.foreignFills[settings.host] !== false) {
|
||||||
filledFields = filledFields.concat(await dispatchFill(tab, fillRequest, true, true, true));
|
filledFields = filledFields.concat(
|
||||||
|
await dispatchFill(settings, fillRequest, true, true, true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!filledFields.length) {
|
if (!filledFields.length) {
|
||||||
@@ -384,10 +403,7 @@ async function handleMessage(settings, message, sendResponse) {
|
|||||||
case "fill":
|
case "fill":
|
||||||
try {
|
try {
|
||||||
// dispatch initial fill request
|
// dispatch initial fill request
|
||||||
var filledFields = await fillFields(settings.tab, message.login, [
|
var filledFields = await fillFields(settings, message.login, ["login", "secret"]);
|
||||||
"login",
|
|
||||||
"secret"
|
|
||||||
]);
|
|
||||||
saveRecent(settings, message.login);
|
saveRecent(settings, message.login);
|
||||||
|
|
||||||
// no need to check filledFields, because fillFields() already throws an error if empty
|
// no need to check filledFields, because fillFields() already throws an error if empty
|
||||||
|
@@ -74,29 +74,38 @@
|
|||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
*
|
*
|
||||||
* @param object request Form fill request
|
* @param object request Form fill request
|
||||||
* @return void
|
* @return object result of filling a form
|
||||||
*/
|
*/
|
||||||
function fillLogin(request) {
|
function fillLogin(request) {
|
||||||
var autoSubmit = false;
|
var autoSubmit = false;
|
||||||
var filledFields = [];
|
var result = {
|
||||||
|
filledFields: [],
|
||||||
|
foreignFill: undefined
|
||||||
|
};
|
||||||
|
|
||||||
// get the login form
|
// get the login form
|
||||||
var loginForm = form();
|
var loginForm = form();
|
||||||
|
|
||||||
// don't attempt to fill non-secret forms unless non-secret filling is allowed
|
// don't attempt to fill non-secret forms unless non-secret filling is allowed
|
||||||
if (!find(PASSWORD_FIELDS, loginForm) && !request.allowNoSecret) {
|
if (!find(PASSWORD_FIELDS, loginForm) && !request.allowNoSecret) {
|
||||||
return filledFields;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure the origin is the same, or ask the user for permissions to continue
|
// ensure the origin is the same, or ask the user for permissions to continue
|
||||||
if (window.location.origin !== request.origin) {
|
if (window.location.origin !== request.origin) {
|
||||||
|
if (!request.allowForeign) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
var message =
|
var message =
|
||||||
"You have requested to fill login credentials into an embedded document from a " +
|
"You have requested to fill login credentials into an embedded document from a " +
|
||||||
"different origin than the main document in this tab. Do you wish to proceed?\n\n" +
|
"different origin than the main document in this tab. Do you wish to proceed?\n\n" +
|
||||||
`Tab origin: ${request.origin}\n` +
|
`Tab origin: ${request.origin}\n` +
|
||||||
`Embedded origin: ${window.location.origin}`;
|
`Embedded origin: ${window.location.origin}`;
|
||||||
if (!request.allowForeign || !confirm(message)) {
|
if (!request.approvedForeign) {
|
||||||
return filledFields;
|
result.foreignFill = confirm(message);
|
||||||
|
if (!result.foreignFill) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +114,7 @@
|
|||||||
request.fields.includes("login") &&
|
request.fields.includes("login") &&
|
||||||
update(USERNAME_FIELDS, request.login.fields.login, loginForm)
|
update(USERNAME_FIELDS, request.login.fields.login, loginForm)
|
||||||
) {
|
) {
|
||||||
filledFields.push("login");
|
result.filledFields.push("login");
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill secret field
|
// fill secret field
|
||||||
@@ -113,7 +122,7 @@
|
|||||||
request.fields.includes("secret") &&
|
request.fields.includes("secret") &&
|
||||||
update(PASSWORD_FIELDS, request.login.fields.secret, loginForm)
|
update(PASSWORD_FIELDS, request.login.fields.secret, loginForm)
|
||||||
) {
|
) {
|
||||||
filledFields.push("secret");
|
result.filledFields.push("secret");
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for multiple password fields in the login form
|
// check for multiple password fields in the login form
|
||||||
@@ -153,7 +162,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// finished filling things successfully
|
// finished filling things successfully
|
||||||
return filledFields;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user