Bug 1426767 - Don't autocomplete logins in documents with a null principal. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D7388

--HG--
rename : toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html => toolkit/components/passwordmgr/test/mochitest/test_autocomplete_sandboxed.html
extra : rebase_source : dcfbdffc9d934365850397df850233db641364a8
extra : source : 8d9d8cde6d8bfa1c13a06b3d73f987bc7267ea0f
This commit is contained in:
Matthew Noorenberghe 2018-10-02 21:34:57 -07:00
parent 7826571e9b
commit a6c0ee093d
3 changed files with 136 additions and 3 deletions

View File

@ -10,7 +10,6 @@ ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Timer.jsm");
ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm");
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
"resource://gre/modules/BrowserUtils.jsm");
@ -18,6 +17,10 @@ ChromeUtils.defineModuleGetter(this, "LoginHelper",
"resource://gre/modules/LoginHelper.jsm");
ChromeUtils.defineModuleGetter(this, "LoginFormFactory",
"resource://gre/modules/LoginManagerContent.jsm");
ChromeUtils.defineModuleGetter(this, "LoginManagerContent",
"resource://gre/modules/LoginManagerContent.jsm");
ChromeUtils.defineModuleGetter(this, "UserAutoCompleteResult",
"resource://gre/modules/LoginManagerContent.jsm");
ChromeUtils.defineModuleGetter(this, "InsecurePasswordUtils",
"resource://gre/modules/InsecurePasswordUtils.jsm");
@ -503,8 +506,25 @@ LoginManager.prototype = {
// aPreviousResult is an nsIAutoCompleteResult, aElement is
// HTMLInputElement
let form = LoginFormFactory.createFromField(aElement);
let isSecure = InsecurePasswordUtils.isFormSecure(form);
let {isNullPrincipal} = aElement.nodePrincipal;
// Show the insecure login warning in the passwords field on null principal documents.
let isSecure = !isNullPrincipal;
// Avoid loading InsecurePasswordUtils.jsm in a sandboxed document (e.g. an ad. frame) if we
// already know it has a null principal and will therefore get the insecure autocomplete
// treatment.
// InsecurePasswordUtils doesn't handle the null principal case as not secure because we don't
// want the same treatment:
// * The web console warnings will be confusing (as they're primarily about http:) and not very
// useful if the developer intentionally sandboxed the document.
// * The site identity insecure field warning would require LoginManagerContent being loaded and
// listening to some of the DOM events we're ignoring in null principal documents. For memory
// reasons it's better to not load LMC at all for these sandboxed frames. Also, if the top-
// document is sandboxing a document, it probably doesn't want that sandboxed document to be
// able to affect the identity icon in the address bar by adding a password field.
if (isSecure) {
let form = LoginFormFactory.createFromField(aElement);
isSecure = InsecurePasswordUtils.isFormSecure(form);
}
let isPasswordField = aElement.type == "password";
let completeSearch = (autoCompleteLookupPromise, { logins, messageManager }) => {
@ -523,6 +543,14 @@ LoginManager.prototype = {
aCallback.onSearchCompletion(results);
};
if (isNullPrincipal) {
// Don't search login storage when the field has a null principal as we don't want to fill
// logins for the `location` in this case.
let acLookupPromise = this._autoCompleteLookupPromise = Promise.resolve({ logins: [] });
acLookupPromise.then(completeSearch.bind(this, acLookupPromise));
return;
}
if (isPasswordField && aSearchString) {
// Return empty result on password fields with password already filled.
let acLookupPromise = this._autoCompleteLookupPromise = Promise.resolve({ logins: [] });

View File

@ -15,6 +15,9 @@ support-files =
[test_autocomplete_https_upgrade.html]
skip-if = toolkit == 'android' # autocomplete
[test_autocomplete_sandboxed.html]
scheme = https
skip-if = toolkit == 'android' # autocomplete
[test_autofill_https_upgrade.html]
skip-if = toolkit == 'android' # Bug 1259768
[test_autofill_sandboxed.html]

View File

@ -0,0 +1,102 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test form field autocomplete in sandboxed documents (null principal)</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
<script type="text/javascript" src="satchel_common.js"></script>
<script type="text/javascript" src="pwmgr_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script>
var chromeScript = runChecksAfterCommonInit();
var setupScript = runInParent(function setup() {
ChromeUtils.import("resource://gre/modules/Services.jsm");
var nsLoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
Ci.nsILoginInfo, "init");
assert.ok(nsLoginInfo != null, "nsLoginInfo constructor");
var login1 = new nsLoginInfo("https://example.com", "", null,
"tempuser1", "temppass1", "uname", "pword");
// try/catch in case someone runs the tests manually, twice.
try {
Services.logins.addLogin(login1);
} catch (e) {
assert.ok(false, "addLogin threw: " + e);
}
});
</script>
<p id="display"></p>
<!-- we presumably can't hide the content for this test. -->
<div id="content">
<iframe id="sandboxed"
sandbox=""
src="form_basic.html"></iframe>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Login Manager: form field autocomplete in sandboxed documents (null principal) **/
let sandboxed = document.getElementById("sandboxed");
let uname;
let pword;
// Check for expected username/password in form.
function checkACForm(expectedUsername, expectedPassword) {
var formID = uname.parentNode.id;
is(uname.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername);
is(pword.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword);
}
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({"set": [
["security.insecure_field_warning.contextual.enabled", true],
]});
let frameWindow = SpecialPowers.wrap(sandboxed).contentWindow;
// Can't use SimpleTest.promiseFocus as it doesn't work with the sandbox.
await SimpleTest.promiseWaitForCondition(() => {
return frameWindow.document.readyState == "complete" && frameWindow.location.href != "about:blank";
}, "Check frame is loaded");
let frameDoc = SpecialPowers.wrap(sandboxed).contentDocument;
uname = frameDoc.getElementById("form-basic-username");
pword = frameDoc.getElementById("form-basic-password");
});
add_task(async function test_no_autofill() {
// Make sure initial form is empty as autofill shouldn't happen in the sandboxed frame.
checkACForm("", "");
let popupState = await getPopupState();
is(popupState.open, false, "Check popup is initially closed");
});
add_task(async function test_autocomplete_warning_no_logins() {
pword.focus();
let shownPromise = promiseACShown();
synthesizeKey("KEY_ArrowDown"); // open the popup
let results = await shownPromise;
let popupState = await getPopupState();
is(popupState.selectedIndex, -1, "Check no entries are selected upon opening");
let expectedMenuItems = [
"This connection is not secure. Logins entered here could be compromised. Learn More",
];
checkArrayValues(results, expectedMenuItems, "Check all menuitems are displayed correctly.");
checkACForm("", "");
});
</script>
</pre>
</body>
</html>