Skip to content

Commit d92c0b0

Browse files
rrrooommmaaaRoman Shevchenko
and
Roman Shevchenko
authored
use OpenPGP.js from web content script, simplify (#5013)
* use OpenPGP.js from web content script - wip * firefox fix * use processAndStoreKeysFromEkmLocally() directly * simplify * added link to external PR * web-stream-tools support in content script (development setup) * production mode in webpack bundling * testing OpenPGP/Stream operations from content script * lint fix * build for manual testing of content script in Firefox * more debug configurations for VSCode * removed duplicate * fix * fix * allow multiple test result divs * clearer test output * niceties * fix Uint8Array problem in Firefox * updated comment * globally patch Uint8Array.subarray() * fix * patch Uint8Array.slice * Added a test for KeyUtil.readBinary() * increase ava cli timeout values --------- Co-authored-by: Roman Shevchenko <[email protected]>
1 parent 08590c7 commit d92c0b0

28 files changed

+1233
-97
lines changed

.semaphore/semaphore.yml

+3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ blocks:
3737
- name: consumer mock - flaky test group
3838
commands:
3939
- npm run-script test_ci_chrome_consumer_flaky
40+
- name: content script tests
41+
commands:
42+
- npm run-script test_ci_chrome_content_scripts
4043
- name: enterprise mock - standard test group
4144
commands:
4245
- npm run-script test_ci_chrome_enterprise

.vscode/launch.json

+62
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@
2121
"outputCapture": "std",
2222
"skipFiles": ["<node_internals>/**/*.js"]
2323
},
24+
{
25+
"type": "node",
26+
"request": "launch",
27+
"name": "Debug buf",
28+
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ava",
29+
"preLaunchTask": "npm: pretest-incremental",
30+
"runtimeArgs": ["build/test/test/source/buf.js", "--verbose", "--concurrency=1"],
31+
"outputCapture": "std",
32+
"skipFiles": ["<node_internals>/**/*.js"]
33+
},
2434
{
2535
"type": "node",
2636
"request": "launch",
@@ -81,6 +91,25 @@
8191
"outputCapture": "std",
8292
"skipFiles": ["<node_internals>/**/*.js"]
8393
},
94+
{
95+
"type": "node",
96+
"request": "launch",
97+
"name": "Debug chrome_content_scripts",
98+
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ava",
99+
"preLaunchTask": "npm: pretest-incremental",
100+
"runtimeArgs": [
101+
"build/test/test/source/test.js",
102+
"--verbose",
103+
"--concurrency=1",
104+
"--",
105+
"CONTENT-SCRIPT-TESTS",
106+
"--retry=false",
107+
"--debug",
108+
"--pool-size=1"
109+
],
110+
"outputCapture": "std",
111+
"skipFiles": ["<node_internals>/**/*.js"]
112+
},
84113
{
85114
"type": "node",
86115
"request": "launch",
@@ -120,6 +149,39 @@
120149
],
121150
"outputCapture": "std",
122151
"skipFiles": ["<node_internals>/**/*.js"]
152+
},
153+
{
154+
"type": "node",
155+
"request": "launch",
156+
"name": "Debug tsc-compiler",
157+
"program": "${workspaceFolder}/build/tooling/tsc-compiler.js",
158+
"cwd": "${workspaceFolder}",
159+
"args": ["--project", "conf/tsconfig.streams.json"],
160+
"stopOnEntry": true,
161+
"outputCapture": "std",
162+
"skipFiles": ["<node_internals>/**/*.js"]
163+
},
164+
{
165+
"type": "node",
166+
"request": "launch",
167+
"name": "Debug resolve-modules",
168+
"program": "${workspaceFolder}/build/tooling/resolve-modules.js",
169+
"cwd": "${workspaceFolder}",
170+
"args": ["--project", "./tsconfig.json"],
171+
"stopOnEntry": true,
172+
"outputCapture": "std",
173+
"skipFiles": ["<node_internals>/**/*.js"]
174+
},
175+
{
176+
"type": "node",
177+
"request": "launch",
178+
"name": "Debug bundle-content-scripts",
179+
"program": "${workspaceFolder}/build/tooling/bundle-content-scripts.js",
180+
"cwd": "${workspaceFolder}",
181+
"args": [],
182+
"stopOnEntry": true,
183+
"outputCapture": "std",
184+
"skipFiles": ["<node_internals>/**/*.js"]
123185
}
124186
]
125187
}

conf/webpack.config.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//webpack.config.js
2+
//bundle the web version of @openpgp/web-stream-tools for content script
3+
const path = require('path');
4+
5+
module.exports = {
6+
mode: 'production',
7+
entry: {
8+
main: '../build/generic-extension-wip/lib/streams/streams.js',
9+
},
10+
output: {
11+
library: {
12+
name: 'Stream',
13+
type: 'var',
14+
},
15+
path: path.resolve('../build/generic-extension-wip/lib'),
16+
filename: 'streams_web.js', // <--- Will be compiled to this single file
17+
},
18+
resolve: {
19+
fallback: {
20+
stream: false,
21+
},
22+
extensions: ['.js'],
23+
},
24+
};

extension/js/background_page/background_page.ts

-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { Bm, BrowserMsg } from '../common/browser/browser-msg.js';
77
import { emailKeyIndex } from '../common/core/common.js';
88
import { VERSION } from '../common/core/const.js';
99
import { ExpirationCache } from '../common/core/expiration-cache.js';
10-
import { processAndStoreKeysFromEkmLocally, getLocalKeyExpiration } from '../common/helpers.js';
1110
import { Catch } from '../common/platform/catch.js';
1211
import { AcctStore } from '../common/platform/store/acct-store.js';
1312
import { ContactStore } from '../common/platform/store/contact-store.js';
@@ -60,8 +59,6 @@ console.info('background_process.js starting');
6059
BrowserMsg.bgAddListener('storeGlobalSet', (r: Bm.StoreGlobalSet) => GlobalStore.set(r.values));
6160
BrowserMsg.bgAddListener('storeAcctGet', (r: Bm.StoreAcctGet) => AcctStore.get(r.acctEmail, r.keys));
6261
BrowserMsg.bgAddListener('storeAcctSet', (r: Bm.StoreAcctSet) => AcctStore.set(r.acctEmail, r.values));
63-
BrowserMsg.bgAddListener('processAndStoreKeysFromEkmLocally', processAndStoreKeysFromEkmLocally);
64-
BrowserMsg.bgAddListener('getLocalKeyExpiration', getLocalKeyExpiration);
6562

6663
// todo - when https://github.com/FlowCrypt/flowcrypt-browser/issues/2560
6764
// is fixed, this can be moved to the gmail content script, and some may be removed

extension/js/common/browser/browser-msg.ts

+1-17
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,6 @@ export namespace Bm {
8383
export type ShowAttachmentPreview = { iframeUrl: string };
8484
export type ReRenderRecipient = { email: string };
8585
export type SaveFetchedPubkeys = { email: string; pubkeys: string[] };
86-
export type ProcessAndStoreKeysFromEkmLocally = { acctEmail: string; decryptedPrivateKeys: string[] };
87-
export type GetLocalKeyExpiration = { acctEmail: string };
8886

8987
export namespace Res {
9088
export type GetActiveTabInfo = {
@@ -107,12 +105,6 @@ export namespace Bm {
107105
export type AjaxGmailAttachmentGetChunk = { chunk: Buf };
108106
export type _tab_ = { tabId: string | null | undefined }; // eslint-disable-line @typescript-eslint/naming-convention
109107
export type SaveFetchedPubkeys = boolean;
110-
export type GetLocalKeyExpiration = number | undefined;
111-
export type ProcessAndStoreKeysFromEkmLocally = {
112-
needPassphrase?: boolean;
113-
updateCount?: number;
114-
noKeysSetup?: boolean;
115-
};
116108
// eslint-disable-next-line @typescript-eslint/no-explicit-any
117109
export type Db = any; // not included in Any below
118110
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -134,9 +126,7 @@ export namespace Bm {
134126
| StoreGlobalSet
135127
| AjaxGmailAttachmentGetChunk
136128
| SaveFetchedPubkeys
137-
| ProcessAndStoreKeysFromEkmLocally
138-
| PgpKeyBinaryToArmored
139-
| GetLocalKeyExpiration;
129+
| PgpKeyBinaryToArmored;
140130
}
141131

142132
export type AnyRequest =
@@ -176,8 +166,6 @@ export namespace Bm {
176166
| ShowAttachmentPreview
177167
| ReRenderRecipient
178168
| SaveFetchedPubkeys
179-
| ProcessAndStoreKeysFromEkmLocally
180-
| GetLocalKeyExpiration
181169
| PgpKeyBinaryToArmored
182170
| AuthWindowResult;
183171

@@ -244,10 +232,6 @@ export class BrowserMsg {
244232
BrowserMsg.sendAwait(undefined, 'pgpKeyBinaryToArmored', bm, true) as Promise<Bm.Res.PgpKeyBinaryToArmored>,
245233
saveFetchedPubkeys: (bm: Bm.SaveFetchedPubkeys) =>
246234
BrowserMsg.sendAwait(undefined, 'saveFetchedPubkeys', bm, true) as Promise<Bm.Res.SaveFetchedPubkeys>,
247-
processAndStoreKeysFromEkmLocally: (bm: Bm.ProcessAndStoreKeysFromEkmLocally) =>
248-
BrowserMsg.sendAwait(undefined, 'processAndStoreKeysFromEkmLocally', bm, true) as Promise<Bm.Res.ProcessAndStoreKeysFromEkmLocally>,
249-
getLocalKeyExpiration: (bm: Bm.GetLocalKeyExpiration) =>
250-
BrowserMsg.sendAwait(undefined, 'getLocalKeyExpiration', bm, true) as Promise<Bm.Res.GetLocalKeyExpiration>,
251235
},
252236
},
253237
passphraseEntry: (dest: Bm.Dest, bm: Bm.PassphraseEntry) => BrowserMsg.sendCatch(dest, 'passphrase_entry', bm),

extension/js/common/helpers.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { ClientConfiguration } from './client-configuration.js';
1010
import { ContactStore } from './platform/store/contact-store.js';
1111
import { KeyStore } from './platform/store/key-store.js';
1212
import { PassphraseStore } from './platform/store/passphrase-store.js';
13-
import { Bm } from './browser/browser-msg.js';
1413
import { PgpPwd } from './core/crypto/pgp/pgp-password.js';
1514

1615
export const isCustomerUrlFesUsed = async (acctEmail: string) => {
@@ -111,9 +110,11 @@ export const processAndStoreKeysFromEkmLocally = async ({
111110
acctEmail,
112111
decryptedPrivateKeys,
113112
ppOptions: originalOptions,
114-
}: Bm.ProcessAndStoreKeysFromEkmLocally & {
113+
}: {
114+
acctEmail: string;
115+
decryptedPrivateKeys: string[];
115116
ppOptions?: PassphraseOptions;
116-
}): Promise<Bm.Res.ProcessAndStoreKeysFromEkmLocally> => {
117+
}) => {
117118
const { unencryptedPrvs } = await parseAndCheckPrivateKeys(decryptedPrivateKeys);
118119
const existingKeys = await KeyStore.get(acctEmail);
119120
let { keysToRetain, newUnencryptedKeysToSave } = await filterKeysToSave(unencryptedPrvs, existingKeys);
@@ -175,7 +176,7 @@ export const processAndStoreKeysFromEkmLocally = async ({
175176
};
176177
};
177178

178-
export const getLocalKeyExpiration = async ({ acctEmail }: Bm.GetLocalKeyExpiration): Promise<Bm.Res.GetLocalKeyExpiration> => {
179+
export const getLocalKeyExpiration = async (acctEmail: string): Promise<number> => {
179180
const kis = await KeyStore.get(acctEmail);
180181
const expirations = await Promise.all(kis.map(async ki => (await KeyUtil.parse(ki.public))?.expiration ?? Number.MAX_SAFE_INTEGER));
181182
return Math.max(...expirations);

extension/js/common/platform/require.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import { MimeParser } from '../core/types/emailjs.js';
2525
import type * as OpenPGP from 'openpgp';
26+
import { Catch } from './catch.js';
2627

2728
type Codec = {
2829
encode: (text: string, mode: 'fatal' | 'html') => string;
@@ -32,7 +33,16 @@ type Codec = {
3233
};
3334

3435
export const requireOpenpgp = (): typeof OpenPGP => {
35-
return (window as unknown as { openpgp: typeof OpenPGP }).openpgp;
36+
if (window !== globalThis && Catch.browser().name === 'firefox') {
37+
// fix Firefox sandbox permission issues as per convo https://github.com/FlowCrypt/flowcrypt-browser/pull/5013#discussion_r1148343995
38+
window.Uint8Array.prototype.subarray = function (...args) {
39+
return new Uint8Array(this).subarray(...args);
40+
};
41+
window.Uint8Array.prototype.slice = function (...args) {
42+
return new Uint8Array(this).slice(...args);
43+
};
44+
}
45+
return (globalThis as unknown as { openpgp: typeof OpenPGP }).openpgp;
3646
};
3747

3848
export const requireMimeParser = (): typeof MimeParser => {

extension/js/content_scripts/webmail/gmail-element-replacer.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import { Gmail } from '../../common/api/email-provider/gmail/gmail.js';
1616
import { Injector } from '../../common/inject.js';
1717
import { PubLookup } from '../../common/api/pub-lookup.js';
1818
import { Notifications } from '../../common/notifications.js';
19-
// note: only types are supported for OpenPGP.js or web-stream-tools, their function calls will fail
2019
import { PgpArmor } from '../../common/core/crypto/pgp/pgp-armor.js';
2120
import { Ui } from '../../common/browser/ui.js';
2221
import { WebmailCommon } from '../../common/webmail.js';
@@ -391,7 +390,7 @@ export class GmailElementReplacer implements WebmailElementReplacer {
391390
if (this.debug) {
392391
console.debug('processNewPgpAttachments() -> msgGet may take some time');
393392
}
394-
const msg = await this.gmail.msgGet(msgId, 'full');
393+
const msg = await this.gmail.msgGet(msgId, 'full'); // todo: cache or thoroughly refactor in #5022
395394
if (this.debug) {
396395
console.debug('processNewPgpAttachments() -> msgGet done -> processAttachments', msg);
397396
}

extension/js/content_scripts/webmail/setup-webmail-content-script.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { Ui } from '../../common/browser/ui.js';
1414
import { ClientConfiguration, ClientConfigurationError } from '../../common/client-configuration.js';
1515
import { Str, Url } from '../../common/core/common.js';
1616
import { InMemoryStoreKeys, VERSION } from '../../common/core/const.js';
17+
import { getLocalKeyExpiration, processAndStoreKeysFromEkmLocally } from '../../common/helpers.js';
1718
import { Injector } from '../../common/inject.js';
1819
import { Lang } from '../../common/lang.js';
1920
import { Notifications } from '../../common/notifications.js';
@@ -291,7 +292,7 @@ export const contentScriptSetupIfVacant = async (webmailSpecific: WebmailSpecifi
291292
ppEvent: { entered?: boolean }
292293
) => {
293294
try {
294-
const { needPassphrase, updateCount, noKeysSetup } = await BrowserMsg.send.bg.await.processAndStoreKeysFromEkmLocally({
295+
const { needPassphrase, updateCount, noKeysSetup } = await processAndStoreKeysFromEkmLocally({
295296
acctEmail,
296297
decryptedPrivateKeys,
297298
});
@@ -382,7 +383,7 @@ export const contentScriptSetupIfVacant = async (webmailSpecific: WebmailSpecifi
382383
};
383384

384385
const notifyExpiringKeys = async (acctEmail: string, clientConfiguration: ClientConfiguration, notifications: Notifications) => {
385-
const expiration = await BrowserMsg.send.bg.await.getLocalKeyExpiration({ acctEmail });
386+
const expiration = await getLocalKeyExpiration(acctEmail);
386387
if (expiration === undefined) {
387388
return;
388389
}

extension/manifest.json

+6-20
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,13 @@
4040
],
4141
"content_scripts": [
4242
{
43-
"matches": [
44-
"https://mail.google.com/*"
45-
],
46-
"css": [
47-
"/css/webmail.css",
48-
"/css/sweetalert2.css"
49-
],
50-
"js": [
51-
"/lib/purify.js",
52-
"/lib/jquery.min.js",
53-
"/lib/sweetalert2.js",
54-
"/js/content_scripts/webmail_bundle.js"
55-
]
43+
"matches": ["https://mail.google.com/*"],
44+
"css": ["/css/webmail.css", "/css/sweetalert2.css"],
45+
"js": ["/lib/purify.js", "/lib/jquery.min.js", "/lib/openpgp.js", "/lib/sweetalert2.js", "/lib/streams_web.js", "/js/content_scripts/webmail_bundle.js"]
5646
},
5747
{
58-
"matches": [
59-
"https://www.google.com/robots.txt*"
60-
],
61-
"js": [
62-
"/js/common/oauth2/oauth2_inject.js"
63-
],
48+
"matches": ["https://www.google.com/robots.txt*"],
49+
"js": ["/js/common/oauth2/oauth2_inject.js"],
6450
"run_at": "document_start"
6551
}
6652
],
@@ -93,4 +79,4 @@
9379
],
9480
"minimum_chrome_version": "96",
9581
"content_security_policy": "upgrade-insecure-requests; script-src 'self'; frame-ancestors https://mail.google.com 'self'; img-src 'self' https://* data: blob:; frame-src 'self' blob:; worker-src 'self'; form-action 'none'; media-src 'none'; font-src 'none'; manifest-src 'none'; object-src 'none'; base-uri 'self'"
96-
}
82+
}

0 commit comments

Comments
 (0)