head.js (3826B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { IPProtectionService, IPProtectionStates } = ChromeUtils.importESModule( 8 "moz-src:///browser/components/ipprotection/IPProtectionService.sys.mjs" 9 ); 10 const { IPPProxyManager, IPPProxyStates } = ChromeUtils.importESModule( 11 "moz-src:///browser/components/ipprotection/IPPProxyManager.sys.mjs" 12 ); 13 const { IPPSignInWatcher } = ChromeUtils.importESModule( 14 "moz-src:///browser/components/ipprotection/IPPSignInWatcher.sys.mjs" 15 ); 16 const { ProxyPass } = ChromeUtils.importESModule( 17 "moz-src:///browser/components/ipprotection/GuardianClient.sys.mjs" 18 ); 19 const { RemoteSettings } = ChromeUtils.importESModule( 20 "resource://services-settings/remote-settings.sys.mjs" 21 ); 22 23 const { sinon } = ChromeUtils.importESModule( 24 "resource://testing-common/Sinon.sys.mjs" 25 ); 26 27 function waitForEvent(target, eventName, callback = () => true) { 28 return new Promise(resolve => { 29 let listener = event => { 30 if (callback()) { 31 target.removeEventListener(eventName, listener); 32 resolve(event); 33 } 34 }; 35 target.addEventListener(eventName, listener); 36 }); 37 } 38 39 async function putServerInRemoteSettings( 40 server = { 41 hostname: "test1.example.com", 42 port: 443, 43 quarantined: false, 44 } 45 ) { 46 const TEST_US_CITY = { 47 name: "Test City", 48 code: "TC", 49 servers: [server], 50 }; 51 const US = { 52 name: "United States", 53 code: "US", 54 cities: [TEST_US_CITY], 55 }; 56 do_get_profile(); 57 const client = RemoteSettings("vpn-serverlist"); 58 await client.db.clear(); 59 await client.db.create(US); 60 await client.db.importChanges({}, Date.now()); 61 } 62 /* exported putServerInRemoteSettings */ 63 64 function setupStubs( 65 sandbox, 66 options = { 67 signedIn: true, 68 isLinkedToGuardian: true, 69 validProxyPass: true, 70 entitlement: { 71 subscribed: false, 72 uid: 42, 73 created_at: "2023-01-01T12:00:00.000Z", 74 }, 75 } 76 ) { 77 sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => options.signedIn); 78 sandbox 79 .stub(IPProtectionService.guardian, "isLinkedToGuardian") 80 .resolves(options.isLinkedToGuardian); 81 sandbox.stub(IPProtectionService.guardian, "fetchUserInfo").resolves({ 82 status: 200, 83 error: null, 84 entitlement: options.entitlement, 85 }); 86 sandbox.stub(IPProtectionService.guardian, "enroll").resolves({ 87 status: 200, 88 error: null, 89 }); 90 sandbox.stub(IPProtectionService.guardian, "fetchProxyPass").resolves({ 91 status: 200, 92 error: undefined, 93 pass: new ProxyPass( 94 options.validProxyPass 95 ? createProxyPassToken() 96 : createExpiredProxyPassToken() 97 ), 98 }); 99 } 100 101 /** 102 * Creates a Token that can be fed as a Network Response from Guardian 103 * to simulate a Proxy Pass. 104 * 105 * @param {Temporal.Instant} from 106 * @param {Temporal.Instant} until 107 * @returns {string} JWT Token 108 */ 109 function createProxyPassToken( 110 from = Temporal.Now.instant(), 111 until = from.add({ hours: 24 }) 112 ) { 113 const header = { 114 alg: "HS256", 115 typ: "JWT", 116 }; 117 const body = { 118 iat: Math.floor(from.add({ seconds: 1 }).epochMilliseconds / 1000), 119 nbf: Math.floor(from.epochMilliseconds / 1000), 120 exp: Math.floor(until.epochMilliseconds / 1000), 121 sub: "proxy-pass-user-42", 122 aud: "guardian-proxy", 123 iss: "vpn.mozilla.org", 124 }; 125 const encode = obj => btoa(JSON.stringify(obj)); 126 return [encode(header), encode(body), "signature"].join("."); 127 } 128 /* exported createExpiredProxyPassToken */ 129 function createExpiredProxyPassToken() { 130 return createProxyPassToken( 131 Temporal.Now.instant().subtract({ hours: 2 }), 132 Temporal.Now.instant().subtract({ hours: 1 }) 133 ); 134 } 135 /* exported createExpiredProxyPassToken */