1 import type { Store } from 'redux';
3 import { WASM_PROCEDURE_BATCH_SIZE } from '@proton/pass/lib/core/constants';
4 import type { PassCoreProxy } from '@proton/pass/lib/core/types';
5 import { hasDomain, hasOTP } from '@proton/pass/lib/items/item.predicates';
6 import { intoSelectedItem } from '@proton/pass/lib/items/item.utils';
7 import { selectMonitoredLogins } from '@proton/pass/store/selectors';
8 import { type UniqueItem } from '@proton/pass/types';
9 import { and, not } from '@proton/pass/utils/fp/predicates';
10 import { deobfuscate } from '@proton/pass/utils/obfuscate/xor';
11 import chunk from '@proton/utils/chunk';
13 export interface MonitorService {
14 checkMissing2FAs: () => Promise<UniqueItem[]>;
15 checkWeakPasswords: () => Promise<UniqueItem[]>;
18 /** MonitorService provides `PassMonitor` methods that rely
19 * on the `PassRustCore` module */
20 export const createMonitorService = (core: PassCoreProxy, store: Store): MonitorService => {
21 const getLoginItems = () => selectMonitoredLogins(store.getState());
23 const service: MonitorService = {
24 checkMissing2FAs: async () => {
25 const logins = getLoginItems();
26 const candidates = logins.filter(and(hasDomain, not(hasOTP)));
27 const domains = candidates.flatMap((item) => item.data.content.urls);
28 const chunks = chunk(domains, WASM_PROCEDURE_BATCH_SIZE);
30 const results = await Promise.all(chunks.map(core.twofa_domains_eligible));
31 const eligible = new Map(...results.flatMap((dic) => dic.entries()));
32 const eligibleDomain = (url: string) => eligible.get(url) === true;
34 return candidates.filter((item) => item.data.content.urls.some(eligibleDomain)).map(intoSelectedItem);
37 checkWeakPasswords: async () => {
38 const logins = getLoginItems();
39 const candidates = logins.filter((item) => item.data.content.password.v);
40 const passwords = candidates.map((item) => deobfuscate(item.data.content.password));
41 const chunks = chunk(passwords, WASM_PROCEDURE_BATCH_SIZE);
42 const scores = (await Promise.all(chunks.map(core.check_password_scores))).flat();
44 return candidates.filter((_, idx) => scores[idx] !== 'Strong').map(intoSelectedItem);