test_getPotentialBreachesByLoginGUID.js (10290B)
1 /** 2 * Test LoginBreaches.getPotentialBreachesByLoginGUID 3 */ 4 5 "use strict"; 6 7 const { RemoteSettings } = ChromeUtils.importESModule( 8 "resource://services-settings/remote-settings.sys.mjs" 9 ); 10 11 // Initializing BrowserGlue requires a profile on Windows. 12 do_get_profile(); 13 14 const gBrowserGlue = Cc["@mozilla.org/browser/browserglue;1"].getService( 15 Ci.nsIObserver 16 ); 17 18 ChromeUtils.defineESModuleGetters(this, { 19 LoginBreaches: "resource:///modules/LoginBreaches.sys.mjs", 20 }); 21 22 const TEST_BREACHES = [ 23 { 24 AddedDate: "2018-12-20T23:56:26Z", 25 BreachDate: "2018-12-16", 26 Domain: "breached.com", 27 Name: "Breached", 28 PwnCount: 1643100, 29 DataClasses: ["Email addresses", "Usernames", "Passwords", "IP addresses"], 30 _status: "synced", 31 id: "047940fe-d2fd-4314-b636-b4a952ee0043", 32 last_modified: "1541615610052", 33 schema: "1541615609018", 34 }, 35 { 36 AddedDate: "2018-12-20T23:56:26Z", 37 BreachDate: "2018-12-16", 38 Domain: "breached-subdomain.host.com", 39 Name: "Only a Sub-Domain was Breached", 40 PwnCount: 2754200, 41 DataClasses: ["Email addresses", "Usernames", "Passwords", "IP addresses"], 42 _status: "synced", 43 id: "047940fe-d2fd-4314-b636-b4a952ee0044", 44 last_modified: "1541615610052", 45 schema: "1541615609018", 46 }, 47 { 48 AddedDate: "2018-12-20T23:56:26Z", 49 BreachDate: "2018-12-16", 50 Domain: "breached-site-without-passwords.com", 51 Name: "Breached Site without passwords", 52 PwnCount: 987654, 53 DataClasses: ["Email addresses", "Usernames", "IP addresses"], 54 _status: "synced", 55 id: "047940fe-d2fd-4314-b636-b4a952ee0045", 56 last_modified: "1541615610052", 57 schema: "1541615609018", 58 }, 59 ]; 60 61 const CRASHING_URI_LOGIN = LoginTestUtils.testData.formLogin({ 62 origin: "chrome://grwatcher", 63 formActionOrigin: "https://www.example.com", 64 username: "username", 65 password: "password", 66 timePasswordChanged: new Date("2018-12-15").getTime(), 67 }); 68 const NOT_BREACHED_LOGIN = LoginTestUtils.testData.formLogin({ 69 origin: "https://www.example.com", 70 formActionOrigin: "https://www.example.com", 71 username: "username", 72 password: "password", 73 timePasswordChanged: new Date("2018-12-15").getTime(), 74 }); 75 const BREACHED_LOGIN = LoginTestUtils.testData.formLogin({ 76 origin: "https://www.breached.com", 77 formActionOrigin: "https://www.breached.com", 78 username: "username", 79 password: "password", 80 timePasswordChanged: new Date("2018-12-15").getTime(), 81 }); 82 const NOT_BREACHED_SUBDOMAIN_LOGIN = LoginTestUtils.testData.formLogin({ 83 origin: "https://not-breached-subdomain.host.com", 84 formActionOrigin: "https://not-breached-subdomain.host.com", 85 username: "username", 86 password: "password", 87 }); 88 const BREACHED_SUBDOMAIN_LOGIN = LoginTestUtils.testData.formLogin({ 89 origin: "https://breached-subdomain.host.com", 90 formActionOrigin: "https://breached-subdomain.host.com", 91 username: "username", 92 password: "password", 93 timePasswordChanged: new Date("2018-12-15").getTime(), 94 }); 95 const LOGIN_FOR_BREACHED_SITE_WITHOUT_PASSWORDS = 96 LoginTestUtils.testData.formLogin({ 97 origin: "https://breached-site-without-passwords.com", 98 formActionOrigin: "https://breached-site-without-passwords.com", 99 username: "username", 100 password: "password", 101 timePasswordChanged: new Date("2018-12-15").getTime(), 102 }); 103 const LOGIN_WITH_NON_STANDARD_URI = LoginTestUtils.testData.formLogin({ 104 origin: "someApp://random/path/to/login", 105 formActionOrigin: "someApp://random/path/to/login", 106 username: "username", 107 password: "password", 108 timePasswordChanged: new Date("2018-12-15").getTime(), 109 }); 110 111 add_task(async function test_notBreachedLogin() { 112 await Services.logins.addLoginAsync(NOT_BREACHED_LOGIN); 113 const breachesByLoginGUID = 114 await LoginBreaches.getPotentialBreachesByLoginGUID( 115 [NOT_BREACHED_LOGIN], 116 TEST_BREACHES 117 ); 118 Assert.strictEqual( 119 breachesByLoginGUID.size, 120 0, 121 "Should be 0 breached logins." 122 ); 123 }); 124 125 add_task(async function test_breachedLogin() { 126 await Services.logins.addLoginAsync(BREACHED_LOGIN); 127 const breachesByLoginGUID = 128 await LoginBreaches.getPotentialBreachesByLoginGUID( 129 [NOT_BREACHED_LOGIN, BREACHED_LOGIN], 130 TEST_BREACHES 131 ); 132 Assert.strictEqual( 133 breachesByLoginGUID.size, 134 1, 135 "Should be 1 breached login: " + BREACHED_LOGIN.origin 136 ); 137 Assert.strictEqual( 138 breachesByLoginGUID.get(BREACHED_LOGIN.guid).breachAlertURL, 139 "https://monitor.firefox.com/breach-details/Breached?utm_source=firefox-desktop&utm_medium=referral&utm_campaign=about-logins&utm_content=about-logins", 140 "Breach alert link should be equal to the breachAlertURL" 141 ); 142 }); 143 144 add_task(async function test_breachedLoginAfterCrashingUriLogin() { 145 await Services.logins.addLoginAsync(CRASHING_URI_LOGIN); 146 147 const breachesByLoginGUID = 148 await LoginBreaches.getPotentialBreachesByLoginGUID( 149 [CRASHING_URI_LOGIN, BREACHED_LOGIN], 150 TEST_BREACHES 151 ); 152 Assert.strictEqual( 153 breachesByLoginGUID.size, 154 1, 155 "Should be 1 breached login: " + BREACHED_LOGIN.origin 156 ); 157 Assert.strictEqual( 158 breachesByLoginGUID.get(BREACHED_LOGIN.guid).breachAlertURL, 159 "https://monitor.firefox.com/breach-details/Breached?utm_source=firefox-desktop&utm_medium=referral&utm_campaign=about-logins&utm_content=about-logins", 160 "Breach alert link should be equal to the breachAlertURL" 161 ); 162 }); 163 164 add_task(async function test_notBreachedSubdomain() { 165 await Services.logins.addLoginAsync(NOT_BREACHED_SUBDOMAIN_LOGIN); 166 167 const breachesByLoginGUID = 168 await LoginBreaches.getPotentialBreachesByLoginGUID( 169 [NOT_BREACHED_LOGIN, NOT_BREACHED_SUBDOMAIN_LOGIN], 170 TEST_BREACHES 171 ); 172 Assert.strictEqual( 173 breachesByLoginGUID.size, 174 0, 175 "Should be 0 breached logins." 176 ); 177 }); 178 179 add_task(async function test_breachedSubdomain() { 180 await Services.logins.addLoginAsync(BREACHED_SUBDOMAIN_LOGIN); 181 182 const breachesByLoginGUID = 183 await LoginBreaches.getPotentialBreachesByLoginGUID( 184 [NOT_BREACHED_SUBDOMAIN_LOGIN, BREACHED_SUBDOMAIN_LOGIN], 185 TEST_BREACHES 186 ); 187 Assert.strictEqual( 188 breachesByLoginGUID.size, 189 1, 190 "Should be 1 breached login: " + BREACHED_SUBDOMAIN_LOGIN.origin 191 ); 192 }); 193 194 add_task(async function test_breachedSiteWithoutPasswords() { 195 await Services.logins.addLoginAsync( 196 LOGIN_FOR_BREACHED_SITE_WITHOUT_PASSWORDS 197 ); 198 199 const breachesByLoginGUID = 200 await LoginBreaches.getPotentialBreachesByLoginGUID( 201 [LOGIN_FOR_BREACHED_SITE_WITHOUT_PASSWORDS], 202 TEST_BREACHES 203 ); 204 Assert.strictEqual( 205 breachesByLoginGUID.size, 206 0, 207 "Should be 0 breached login: " + 208 LOGIN_FOR_BREACHED_SITE_WITHOUT_PASSWORDS.origin 209 ); 210 }); 211 212 add_task(async function test_breachAlertHiddenAfterDismissal() { 213 BREACHED_LOGIN.guid = "{d2de5ac1-4de6-e544-a7af-1f75abcba92b}"; 214 215 await Services.logins.initializationPromise; 216 const storageJSON = Services.logins.wrappedJSObject._storage; 217 218 storageJSON.recordBreachAlertDismissal(BREACHED_LOGIN.guid); 219 220 const breachesByLoginGUID = 221 await LoginBreaches.getPotentialBreachesByLoginGUID( 222 [BREACHED_LOGIN, NOT_BREACHED_LOGIN], 223 TEST_BREACHES 224 ); 225 Assert.strictEqual( 226 breachesByLoginGUID.size, 227 0, 228 "Should be 0 breached logins after dismissal: " + BREACHED_LOGIN.origin 229 ); 230 231 info("Clear login storage"); 232 Services.logins.removeAllUserFacingLogins(); 233 234 const breachesByLoginGUID2 = 235 await LoginBreaches.getPotentialBreachesByLoginGUID( 236 [BREACHED_LOGIN, NOT_BREACHED_LOGIN], 237 TEST_BREACHES 238 ); 239 Assert.strictEqual( 240 breachesByLoginGUID2.size, 241 1, 242 "Breached login should re-appear after clearing storage: " + 243 BREACHED_LOGIN.origin 244 ); 245 }); 246 247 add_task(async function test_newBreachAfterDismissal() { 248 TEST_BREACHES[0].AddedDate = new Date().toISOString(); 249 250 const breachesByLoginGUID = 251 await LoginBreaches.getPotentialBreachesByLoginGUID( 252 [BREACHED_LOGIN, NOT_BREACHED_LOGIN], 253 TEST_BREACHES 254 ); 255 256 Assert.strictEqual( 257 breachesByLoginGUID.size, 258 1, 259 "Should be 1 breached login after new breach following the dismissal of a previous breach: " + 260 BREACHED_LOGIN.origin 261 ); 262 }); 263 264 add_task(async function test_ExceptionsThrownByNonStandardURIsAreCaught() { 265 await Services.logins.addLoginAsync(LOGIN_WITH_NON_STANDARD_URI); 266 267 const breachesByLoginGUID = 268 await LoginBreaches.getPotentialBreachesByLoginGUID( 269 [LOGIN_WITH_NON_STANDARD_URI, BREACHED_LOGIN], 270 TEST_BREACHES 271 ); 272 273 Assert.strictEqual( 274 breachesByLoginGUID.size, 275 1, 276 "Exceptions thrown by logins with non-standard URIs should be caught." 277 ); 278 }); 279 280 add_task(async function test_setBreachesFromRemoteSettingsSync() { 281 const login = NOT_BREACHED_SUBDOMAIN_LOGIN; 282 const nowExampleIsInBreachedRecords = [ 283 { 284 AddedDate: "2018-12-20T23:56:26Z", 285 BreachDate: "2018-12-16", 286 Domain: "not-breached-subdomain.host.com", 287 Name: "not-breached-subdomain.host.com is now breached!", 288 PwnCount: 1643100, 289 DataClasses: [ 290 "Email addresses", 291 "Usernames", 292 "Passwords", 293 "IP addresses", 294 ], 295 _status: "synced", 296 id: "047940fe-d2fd-4314-b636-b4a952ee0044", 297 last_modified: "1541615610052", 298 schema: "1541615609018", 299 }, 300 ]; 301 async function emitSync() { 302 await RemoteSettings(LoginBreaches.REMOTE_SETTINGS_COLLECTION).emit( 303 "sync", 304 { data: { current: nowExampleIsInBreachedRecords } } 305 ); 306 } 307 308 const beforeSyncBreachesByLoginGUID = 309 await LoginBreaches.getPotentialBreachesByLoginGUID([login]); 310 Assert.strictEqual( 311 beforeSyncBreachesByLoginGUID.size, 312 0, 313 "Should be 0 breached login before not-breached-subdomain.host.com is added to fxmonitor-breaches collection and synced: " 314 ); 315 gBrowserGlue.observe(null, "browser-glue-test", "add-breaches-sync-handler"); 316 const db = RemoteSettings(LoginBreaches.REMOTE_SETTINGS_COLLECTION).db; 317 await db.importChanges({}, Date.now(), [nowExampleIsInBreachedRecords[0]]); 318 await emitSync(); 319 320 const breachesByLoginGUID = 321 await LoginBreaches.getPotentialBreachesByLoginGUID([login]); 322 Assert.strictEqual( 323 breachesByLoginGUID.size, 324 1, 325 "Should be 1 breached login after not-breached-subdomain.host.com is added to fxmonitor-breaches collection and synced: " 326 ); 327 });