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