/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; class CookieValidatedObserver { #promise; static waitForCookieValidation() { return new Promise(resolve => { new CookieValidatedObserver(resolve); }); } constructor(promise) { this.#promise = promise; this.obs = Services.obs; this.obs.addObserver(this, "cookies-validated"); } observe(subject, topic) { if (topic == "cookies-validated") { if (this.obs) { this.obs.removeObserver(this, "cookies-validated"); } this.#promise(); } } } add_task(async function test_invalid_cookie_fix() { let promise = CookieValidatedObserver.waitForCookieValidation(); // Set up a profile. let profile = do_get_profile(); // Start the cookieservice, to force creation of a database. Services.cookies.sessionCookies; // Each cookie-db opening will trigger a validation. await promise; // Close the profile. await promise_close_profile(); // Remove the cookie file in order to create another database file. do_get_cookie_file(profile).remove(false); // Create a schema 16 database. let schema16db = new CookieDatabaseConnection( do_get_cookie_file(profile), 17 ); const nowInMSec = Date.now(); const farFarInThePastInMSec = nowInMSec - 60 * 60 * 24 * 1000 * 1000; const farFarInTheFutureInMSec = nowInMSec + 60 * 60 * 24 * 1000 * 1000; const nearFutureInMSec = nowInMSec + 60 * 60 * 24 * 1000; const nowInUSec = nowInMSec * 1000; const farFarInThePastInUSec = farFarInThePastInMSec * 1000; // CookieValidation.result => eRejectedNoneRequiresSecure schema16db.insertCookie( new Cookie( "test1", "Some data", "foo.com", "/", nearFutureInMSec, nowInUSec, nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_NONE, Ci.nsICookie.SCHEME_UNSET, nowInUSec ) ); // CookieValidation.result => eOK schema16db.insertCookie( new Cookie( "test2", "Some data", "foo.com", "/", nearFutureInMSec, nowInUSec, nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_LAX, Ci.nsICookie.SCHEME_UNSET, nowInUSec ) ); // CookieValidation.result => eOK schema16db.insertCookie( new Cookie( "test3", "Some data", "foo.com", "/", nearFutureInMSec, nowInUSec, nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_STRICT, Ci.nsICookie.SCHEME_UNSET, nowInUSec ) ); // CookieValidation.result => eOK schema16db.insertCookie( new Cookie( "test4", "Some data", "foo.com", "/", nearFutureInMSec, nowInUSec, nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_UNSET, Ci.nsICookie.SCHEME_UNSET, nowInUSec ) ); // CookieValidation.result => eRejectedNoneRequiresSecure schema16db.insertCookie( new Cookie( "test5", "Some data", "foo.com", "/", nearFutureInMSec, nowInUSec, nowInUSec, false, true, false, false, {}, Ci.nsICookie.SAMESITE_NONE, Ci.nsICookie.SCHEME_UNSET, nowInUSec ) ); // CookieValidation.result => eRejectedAttributeExpiryOversize schema16db.insertCookie( new Cookie( "test6", "Some data", "foo.com", "/", farFarInTheFutureInMSec, nowInUSec, nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_UNSET, Ci.nsICookie.SCHEME_UNSET, nowInUSec ) ); // CookieValidation.result => eRejectedEmptyNameAndValue schema16db.insertCookie( new Cookie( "", "", "foo.com", "/", nearFutureInMSec, nowInUSec, nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_UNSET, Ci.nsICookie.SCHEME_UNSET, nowInUSec ) ); // CookieValidation.result => eRejectedInvalidCharName schema16db.insertCookie( new Cookie( " test8", "", "foo.com", "/", nearFutureInMSec, nowInUSec, nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_UNSET, Ci.nsICookie.SCHEME_UNSET, nowInUSec ) ); // CookieValidation.result => eRejectedInvalidCharValue schema16db.insertCookie( new Cookie( "test9", " test9", "foo.com", "/", nearFutureInMSec, nowInUSec, nowInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_UNSET, Ci.nsICookie.SCHEME_UNSET, nowInUSec ) ); // CookieValidation.result => eOK schema16db.insertCookie( new Cookie( "testA", "Some data", "foo.com", "/", nearFutureInMSec, nowInUSec, farFarInThePastInUSec, false, false, false, false, {}, Ci.nsICookie.SAMESITE_UNSET, Ci.nsICookie.SCHEME_UNSET, nowInUSec ) ); schema16db.close(); schema16db = null; // Check if we have the right entries { const dbConnection = Services.storage.openDatabase( do_get_cookie_file(profile) ); const stmt = dbConnection.createStatement( "SELECT name, sameSite, isSecure, creationTime, expiry FROM moz_cookies ORDER BY name" ); const results = []; while (stmt.executeStep()) { results.push({ name: stmt.getString(0), sameSite: stmt.getInt32(1), isSecure: stmt.getInt32(2), creationTime: stmt.getInt64(3), expiry: stmt.getInt64(4), }); } Assert.deepEqual(results, [ { name: "", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: " test8", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: "test1", sameSite: Ci.nsICookie.SAMESITE_NONE, isSecure: 0, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: "test2", sameSite: Ci.nsICookie.SAMESITE_LAX, isSecure: 0, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: "test3", sameSite: Ci.nsICookie.SAMESITE_STRICT, isSecure: 0, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: "test4", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: "test5", sameSite: Ci.nsICookie.SAMESITE_NONE, isSecure: 1, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: "test6", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, creationTime: nowInUSec, expiry: farFarInTheFutureInMSec, }, { name: "test9", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: "testA", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, creationTime: farFarInThePastInUSec, expiry: nearFutureInMSec, }, ]); stmt.finalize(); dbConnection.close(); } promise = CookieValidatedObserver.waitForCookieValidation(); // Reload profile. await promise_load_profile(); await promise; // Assert inserted cookies are in the db and correctly handled by services. Assert.equal(Services.cookies.countCookiesFromHost("foo.com"), 7); // Close the profile. await promise_close_profile(); // Check if the sameSite issues were fixed { const dbConnection = Services.storage.openDatabase( do_get_cookie_file(profile) ); const stmt = dbConnection.createStatement( "SELECT name, sameSite, isSecure, creationTime, expiry FROM moz_cookies ORDER BY name" ); const results = []; while (stmt.executeStep()) { results.push({ name: stmt.getString(0), sameSite: stmt.getInt32(1), isSecure: stmt.getInt32(2), creationTime: stmt.getInt64(3), expiry: stmt.getInt64(4), }); } Assert.deepEqual(results, [ { name: "test1", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: "test2", sameSite: Ci.nsICookie.SAMESITE_LAX, isSecure: 0, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: "test3", sameSite: Ci.nsICookie.SAMESITE_STRICT, isSecure: 0, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: "test4", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: "test5", sameSite: Ci.nsICookie.SAMESITE_NONE, isSecure: 1, creationTime: nowInUSec, expiry: nearFutureInMSec, }, { name: "test6", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, creationTime: nowInUSec, expiry: results.find(a => a.name === "test6").expiry, }, { name: "testA", sameSite: Ci.nsICookie.SAMESITE_UNSET, isSecure: 0, creationTime: farFarInThePastInUSec, expiry: nearFutureInMSec, }, ]); for (const r of results) { Assert.less(r.expiry, farFarInTheFutureInMSec); } stmt.finalize(); dbConnection.close(); } // Cleanup await promise_load_profile(); Services.cookies.removeAll(); do_close_profile(); });