Skip to content

Security review ​

Reviewed on 2026-10-25 (#112), against Electron's security checklist and the extension threat model of ADR 0004. Each answer names the code that enforces it and the tests that prove it. Repeat the review before each release that changes a trust boundary.

Electron's checklist ​

#ItemStatusHow YapYap meets it
1Only load secure contentMetWhatsApp loads from https://web.whatsapp.com (whatsapp-origin.ts); YapYap's pages from app://, extensions from extension://. The test-only origin override takes loopback only (ADR 0008, C2).
2No Node.js integration for remote pagesMetnodeIntegration, in workers and subframes too, is off in rendererPreferences; e2e/launch.spec.ts checks WhatsApp has no require or process.
3Context isolationMetOn for every renderer; preloads expose only typed contextBridge APIs.
4Process sandboxingMetsandbox: true everywhere and app.enableSandbox(); YapYap refuses to start unsandboxed (sandbox-check.ts). e2e/launch.spec.ts and the packaged smoke test check every renderer.
5Handle permission requestsMetDeny by default; WhatsApp's own origin gets an allowlist, screen capture needs a user pick (permissions/, ADR 0001, 0008 C6 and C15). Tested in permission-policy.test.ts, session-guard.test.ts and E2E.
6Keep webSecurityMetcreateSecureWindow applies the hardened preferences last, and lint forbids creating windows anywhere else; windows.test.ts shows smuggled preferences are ignored.
7Content Security PolicyMetStrict CSP headers on app:// (chrome/assets.ts) and extension:// (extension-files.ts). WhatsApp keeps its own.
8No allowRunningInsecureContentMetOff in rendererPreferences.
9No experimental featuresMetOff in rendererPreferences.
10No enableBlinkFeaturesMetNever set; RendererOptions only takes a preload, a partition and spell checking.
11<webview> allowpopupsN/AwebviewTag is off and every will-attach-webview is refused (web-contents-guards.ts).
12<webview> optionsN/AAs above.
13Limit navigationMetThe main frame stays on its home origin; other links go through the link policy, and redirects off the origin are stopped (navigation/). Subframes are left to WhatsApp's CSP and the sandbox, by design (ADR 0008, C14).
14Limit new windowsMetsetWindowOpenHandler on every web contents: extensions open none, WhatsApp only its own-origin call windows (ADR 0008, C21), and everything else goes through the link policy.
15shell.openExternal with untrusted URLsMetUntrusted links pass classifyLink, which allows only http(s), a checked mailto: and a digits-only tel:; every other call builds its URL from a constant.
16Current ElectronMetPinned to the latest stable release; Dependabot proposes updates weekly.
17Validate IPC sendersMetEvery chrome handler checks the sender's zone, frame origin and payload (chrome/ipc.ts); WhatsApp's notification and share messages check the account and origin. Extensions have no IPC, only one MessagePort.
18Custom protocols, not file://Metapp:// and per-extension extension://, both refusing paths outside their folder; the file-protocol fuse is off.
19FusesMetRunAsNode, NODE_OPTIONS and the inspector are off; cookie encryption, ASAR integrity and ASAR-only loading are on. verify-fuses.ts checks every package in CI.
20No Electron APIs for web contentMetBridges wrap fixed channels and never pass the IPC event; the extension preload forwards only a port. e2e/malicious-extension.spec.ts shows a hostile extension reaches no such API.

Extension threat model ​

ADR 0004 assumes an extension may be fully hostile. Each thing it must not do:

An extension must notWhat stops itProof
Read anything it was not grantedEvery call is checked against the grants for the account on screen; chat and draft text only come through chat:read-active and composer:read-draft, capped and never logged (C26, C28).extensions.spec.ts, host-methods.test.ts, grants.test.ts
Send messagesNo extension operation sends; drafts are typed into the composer for the user to send. The assistant bar sends only on the user's click on a suggestion, after checking the chat is unchanged (ADR 0013).malicious-extension.spec.ts (sendApi, sendViaPort)
Reach hosts it did not declareThe extension CSP and the network filter allow only approved HTTPS hosts, asking before a new one, and loopback only with local-network (C29).network-policy.test.ts, malicious-extension.spec.ts, extensions.spec.ts
Read other extensions' dataEach extension has its own session and partition; secrets and storage are keyed by the ID YapYap knows, never one the extension names.malicious-extension.spec.ts, key-value-store.test.ts
Pass for WhatsApp or YapYapExtensions draw nothing in the main window; menu items and window titles carry the extension's name, set by YapYap (C25).extensions.spec.ts, menu.test.ts
Exhaust the computerCalls are rate limited and time out; a renderer over its CPU or memory budget is restarted, then stopped.rpc.test.ts, resource-guard.test.ts, malicious-extension.spec.ts

Findings ​

#FindingDisposition
1The screen-share "ended" message skipped the sender check that "started" makes.Fixed: both now hear only WhatsApp's own page of an account (capture/shares.ts).
2The extension CSP lacked base-uri, form-action and frame-ancestors, which YapYap's own pages set.Fixed: all three are 'none'.
3Packaged builds honour YAPYAP_TEST_WHATSAPP_ORIGIN.Accepted: loopback HTTP only, logged when used, and environment variables are trusted (C2).
4Subframes may navigate anywhere.Accepted: WhatsApp plays link previews in third-party iframes; the sandbox and its CSP confine them (C14).
5With composer:insert-draft, an extension may replace the user's draft, not only add to it.Accepted: the grant is to write drafts, which the user still reads before sending; documented in ADR 0004.

Released under the MIT License. Development supported by Ensita. YapYap is unofficial and not affiliated with WhatsApp or Meta.