Add autonomous webhooks and dynamic WebView discovery

This commit is contained in:
Ops Agent committed 2026-10-11 08:43:39 +02:00
1 parent ab69f287b6
commit 97f874d183
29 files changed
+3044 -1489

No files matched your search

+10 -2
View File
@@ -4,7 +4,7 @@
The portable launcher verifies and decompresses its embedded payload, then imports the Python package directly from the in-memory ZIP. It uses the same CLI and modules as `python -m whatsapp_suite`.
For a core live operation, Python uses Windows APIs through `ctypes`. It verifies the helper DLL, records application/WebView compatibility, installs a temporary `WH_GETMESSAGE` hook on the Desktop UI thread, and passes a bounded JavaScript request through shared memory. The native helper locates the existing WebView object using build-specific signatures and calls its `ExecuteScriptAsync` interface. A thread message wakes the hook. Results return through the shared mapping; the hook, mapping and process handles are then released. Reports record cleanup outcomes.
For a core live operation, Python uses Windows APIs through `ctypes`. It verifies the helper DLL, records application/WebView compatibility, installs a temporary `WH_GETMESSAGE` hook on the Desktop UI thread, and passes a bounded JavaScript request through shared memory. The native helper discovers function/vtable locations from relocation-free binary fingerprints and locates the existing WebView object using those dynamically resolved addresses and calls its `ExecuteScriptAsync` interface. A thread message wakes the hook. Results return through the shared mapping; the hook, mapping and process handles are then released. Reports record cleanup outcomes.
This still uses native C++ DLLs. Python packages their bytes; it does not replace their implementation with pure Python. The C++ source is for maintenance and is unnecessary on a machine running the portable file. SHA-256 checks verify embedded asset integrity, not publisher identity or authenticity of a downloaded file.
@@ -14,7 +14,7 @@ Contact export reads selected public fields from the local contact table. Sendin
## Boundaries
Live native operations were tested with Desktop build `2.2639.100.0`. Other versions are attempted with a warning. The helper still uses fixed offsets and native object/method checks derived from the tested WebView DLL. A changed layout may fail discovery or cause process instability; removing the version gate does not make those offsets version-independent. Updates require new evidence and validation to be considered tested. Core bridge commands do not use CDP or UI automation; optional legacy commands have their own transports and dependencies. RAM readers only see loaded models. Exported message text does not include media file downloads. Password or passkey private-key extraction is not an implemented feature.
Live native operations were tested with Desktop build `2.2639.100.0`. Other versions are attempted with a warning. The helper resolves function and vtable addresses dynamically, but retains function fingerprints, an object field offset and the exact interface ABI derived from the tested WebView DLL. A changed layout may fail discovery or cause process instability; dynamic address discovery does not make all compiler/object layouts compatible. Updates require new evidence and validation to be considered tested. Core bridge commands do not use CDP or UI automation; optional legacy commands have their own transports and dependencies. RAM readers only see loaded models. Exported message text does not include media file downloads. Password or passkey private-key extraction is not an implemented feature.
The repository includes only maintained source, synthetic fixtures and required runtime assets. Local profiles, extracted application code, credentials, messages, contacts and research dumps are excluded.
@@ -25,3 +25,11 @@ The observed implementation uses documented Windows hook loading and WebView scr
However, loading a helper into another process and locating internal objects by memory layout is reverse-engineered process instrumentation. WhatsApp does not expose the internal module calls used here as a documented external automation API. Calling documented OS/WebView APIs does not establish that this overall integration is supported or intended by WhatsApp, nor is this observation a complete security audit.
References: [Microsoft SetWindowsHookExW](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexw), [Microsoft WebView2 JavaScript execution](https://learn.microsoft.com/en-us/microsoft-edge/webview2/how-to/javascript).
## Autonomous webhook relay
The explicit `webhook install` command registers JavaScript message collection observers and installs a separate resident helper. A UI-thread timer runs short queue-peek scripts; a WinHTTP worker posts bounded batches to the configured HTTPS endpoint or `127.0.0.1:port/whatsapp-webhook`, without proxies or redirects. HTTPS uses certificate validation and a token header. Queue entries are removed after HTTP 2xx. The built-in loopback receiver acknowledges after SQLite commit and deduplicates message IDs.
The installer exits and releases its temporary hooks. The webhook helper retains one module reference so native callbacks remain valid; stop cancels the timer and releases WebView references but leaves an idle DLL loaded until app exit. This is intentional and distinct from the short-lived test/export helpers' unload checks.
App process exit destroys both the injected JavaScript and resident relay. For direct HTTPS, `--auto-restore` registers a per-user Windows task that runs a short `webhook ensure` check every minute. It uses a DPAPI-protected token and does not keep Python or a local server running between checks. Alternatively, a loopback receiver started with `--restore-config` checks every five seconds. Saved configuration persists; injected process memory does not. See [webhook guide](WEBHOOKS.md) for limits.
+4 -3
View File
@@ -3,7 +3,7 @@
## Portable single-file bundle
Copy **`whatsapp.py`** to another writable folder. That one Python file includes
all tools, both native helpers and the regression fixtures. No adjacent source
all tools, the native helpers and the regression fixtures. No adjacent source
package, DLLs, C++ compiler or build script is needed to run it.
```powershell
@@ -68,8 +68,8 @@ python -m whatsapp_suite send-self
Live WebView probes require Windows x64 and 64-bit Python. Desktop build
`2.2639.100.0` and its recorded WebView2 Core DLL were tested. Other versions or
DLL hashes produce a warning and are attempted. The helper still checks native
object signatures and methods before executing JavaScript; changed layouts can
DLL hashes produce a warning and are attempted. The helper discovers function and vtable locations from binary fingerprints and checks native
objects and methods before executing JavaScript; changed layouts can
cause failure or process instability. The JSON report includes detected/tested
compatibility metadata and cleanup status. `--pid` selects a Desktop host.
@@ -90,6 +90,7 @@ Every command supports `--help` for its options.
| `analyze` | Storage analysis, optionally using an existing loopback CDP endpoint |
| `history-export` | Export local history through Desktop's own database decryption |
| `export-zip` | Export all local message rows to ZIP through JavaScript on WebView, without CDP or UI automation |
| `webhook` | Install/status/stop, direct HTTPS or loopback relay, ensure/auto-restore and optional watch/serve; see [webhook guide](WEBHOOKS.md) |
| `contacts-export` | Export all local contact rows to JSON through the app's JS database getter |
| `send-self` | Send one fixed test message to the signed-in account itself and verify stored plaintext |
| `ram-read` | Read loaded V8 message objects from process memory |
+14
View File
@@ -19,3 +19,17 @@ Run `python whatsapp.py test` for portable checks, or `python -m whatsapp_suite
## Advisory compatibility update — 2026-10-11
Source and rebuilt portable bundle each passed 32 offline tests, including acceptance of untested version/hash combinations and recognition of the tested configuration. A portable live arithmetic probe passed on the original tested build, with successful native cleanup. No other installed Desktop version was available for a live check. The optional Frida SQLite agent's version guard is also advisory; its changed metadata/warning path has not been retested live.
## Dynamic discovery and webhook update — 2026-10-11
Both rebuilt native helpers passed eight arithmetic cases using dynamic discovery on the existing Core DLL. The offline checker discovered its vtables/function and rejected an unrelated kernel32 DLL. This establishes discovery for one known binary, not cross-version compatibility.
An installed webhook delivered a self-message via native HTTP after installer exit with exact plaintext equality. The receiver restored the listener after a normal WhatsApp restart into a new PID, and a second self-message matched in SQLite. Receipts and payloads remain in ignored local analysis files. The installer is not kept running; the HTTP receiver is required, and its optional bootstrap reinjects after app restarts.
Source and isolated portable suites passed 41 tests, including event filtering/decoding/deduplication, bounded text and stop, durable HTTP acknowledgement, duplicate delivery, failure acknowledgement, restoration ownership, direct HTTPS validation, DPAPI token roundtrip and exclusion of transport credentials from listener JavaScript. Direct JavaScript localhost fetch was blocked by connect-src; native HTTP succeeded. No live incoming message from another account or AI summarizer was tested.
Direct HTTPS delivered two further self-messages to the authenticated Node-RED endpoint. Both message IDs and plaintext SHA-256 hashes matched the send receipts. Between messages, WhatsApp restarted from PID 16340 to 20292; the per-user minute task restored the listener automatically and exited with result 0 using the DPAPI-protected configuration. No local HTTP receiver or continuous Python watcher was running. Stop, disabled ensure and reactivation without an environment token passed. Duplicate native wake messages previously overwrote a successful request; the completed-state guard fixed the observed installation failure. Windows reboot and other Desktop versions were not live tested. The Node-RED test flow routes to an unconnected summarizer link and deduplicates in memory only.
The final 212,007-byte bundle was also checked live: 313 contact rows exported, five oldest message records read with native cleanup, and Desktop search found the self-test messages. A complete ZIP contained 9,745 message rows with zero decode errors, valid ZIP CRCs and exact plaintext equality for the post-restart test message. Rebuilding unchanged inputs produced the identical SHA-256 `76c28343eb58a93c4b6251bf1981109df25daab02e3d47f2748dfaa34501e890`. The root workspace copy matches this bundle. Optional CDP/Frida/pairing research commands were not all rerun live.
REA model summary: ev_d05f0cfdcf6fe78605cbe180c38bc8154e306a9b138fd284580cefa995afe683, parent ev_6186f4a795decd53dc3dde7935f9f6dd6bb2d2bb32816e7640842d5d41010d3d. Static coverage has dynamic unknowns; listener behavior is established separately by the live self-message tests.
+64
View File
@@ -0,0 +1,64 @@
# Message webhooks
## Execution and restart behavior
`webhook install` is a one-shot command. JavaScript subscribes to message collection events inside WhatsApp and queues decoded records. A resident native helper posts them directly to an HTTPS endpoint or to a loopback HTTP receiver. Direct HTTPS needs no local server and no continuously running Python process. No CDP or UI automation is used. WhatsApp's CSP blocks direct JavaScript HTTP requests to localhost, so delivery uses native WinHTTP without weakening CSP.
A WhatsApp process restart destroys injected code. For direct HTTPS, optional `--auto-restore` registers a per-user Windows scheduled task that runs a short restoration check every minute while the user is logged in. No administrator rights are requested. Keep Python, the bundle and configuration at their registered paths. The task uses current-user DPAPI to recover the token without an environment variable. The protected configuration cannot be moved to another Windows user/machine. The listener is automatically reinstalled; process memory does not survive restart. Windows reboot behavior follows the scheduled task's normal logged-in scheduling and was not live tested. WhatsApp's package and startup files are not patched.
## Start and stop
For direct HTTPS, supply a 32–128 character printable ASCII token through the named environment variable using your secret manager, then run:
```powershell
python whatsapp.py webhook install --endpoint https://your-endpoint.example/events --token-env WHATSAPP_INJECTOR_WEBHOOK_TOKEN --self-only --auto-restore --config listener.json
```
The endpoint must authenticate `X-Webhook-Token` and accept the batch schema below. Certificate validation is enabled; credentials in URLs, query strings, fragments and redirects are rejected. Remove `--self-only` only when you intend to forward events from other chats. `webhook auto-restore --config listener.json` registers/enables restoration separately; `webhook ensure --config listener.json` performs one check. `webhook watch --config listener.json` is an optional continuously running alternative.
For a local durable test receiver:
```powershell
python whatsapp.py webhook install --port 18763 --config listener.json
python whatsapp.py webhook serve --restore-config listener.json --store events.sqlite3
```
Keep the receiver running; the installer exits. Use `--self-only` on installation when testing just your own self-chat. Otherwise it subscribes to all qualifying local message events. Installation refuses an existing configuration unless you pass `--restore` or choose a new path. The receiver restores the configured listener when WhatsApp starts and does not launch WhatsApp itself.
```powershell
python whatsapp.py webhook status
python whatsapp.py send-self --output self-send-receipt.json
```
`send-self` sends a real fixed test message and must be invoked explicitly. It does not retry uncertain sends. The receiver health endpoint is `http://127.0.0.1:18763/health`; it reports counts and restoration state without message content.
Stop and disable restoration:
```powershell
python whatsapp.py webhook stop --config listener.json
```
Then stop the receiver with Ctrl+C. Re-enable with `webhook install --restore --config listener.json`. Stopping only the injected listener without disabling a restoring configuration causes the receiver to reinstall it.
## Payload and integration
The loopback endpoint is `POST /whatsapp-webhook` on `127.0.0.1`; direct HTTPS uses the explicitly configured URL. Batches include `listener_id`, `seqs` and `events`. Each message event includes a sequence number, message ID, chat ID, timestamp, direction, message type, body/caption and text-length/truncation metadata. The built-in receiver persists events in SQLite; an external receiver can feed them to a summarizer. No AI provider or automatic replies are configured.
HTTP 2xx means the batch was accepted. The built-in receiver commits before acknowledgement and deduplicates message IDs. Failed HTTP delivery leaves queued records available for retry. Consumer side effects such as AI jobs should also deduplicate IDs. There is no exactly-once guarantee.
## Bounds and limitations
- This listens to the app's loaded message collection, not a server-side subscription. Messages not surfaced there can be missed.
- Reinstallation reconciles qualifying loaded models since the saved install timestamp. It is not a complete offline database catch-up. Receiver IDs deduplicate replays.
- Queues are in memory and lost when WhatsApp exits. A full queue of 1,000 events drops new records and exposes a status counter.
- Each event includes up to 12,000 body and 4,000 caption UTF-16 code units. Longer text is marked `text_truncated`; full-message ZIP export is unaffected.
- Batches contain at most 16 events within a native JSON-size bound. Unusually large escaped records may exceed that bound and stall delivery; oversized records are not silently acknowledged.
- Text rows are retried briefly while stored/decoded. Persistent decode failures appear in status counters.
- Edits, deletions and media downloads are not separate webhook features; message IDs deduplicate repeated notifications.
- Stop cancels the native timer; an idle DLL remains loaded until WhatsApp exits to keep pending callbacks safe.
- Auto-restore has a polling/startup gap. Messages during that gap are not guaranteed to be delivered.
- Only the recorded Desktop/WebView build was live tested. Address discovery accepts matching fingerprints, not every future compiler or ABI layout.
## Observed validation
On 2026-10-11, two self-messages reached the local receiver, including one after receiver-owned restoration following a WhatsApp restart, with exact plaintext equality. Two further self-messages reached the direct HTTPS Node-RED endpoint, including one after the scheduled task restored the listener in a new WhatsApp process. Node-RED message IDs and SHA-256 hashes matched the sent plaintext. The task exited with result 0 and used the protected configuration without an injected environment token. Stop, disabled restoration and reactivation were also checked. These were self-chat tests, not live incoming-message tests from another account. Incoming-event filtering is covered by synthetic fixtures. The Node-RED test flow deduplicates in memory; it is not a durable message archive or an AI summarizer.
+5 -3
View File
@@ -1,8 +1,10 @@
{
"note": "Runtime asset hashes; integrity only, not a publisher signature",
"sha256": {
"math_probe.dll": "136620d96f4745a4c41edfe1f4a9272ef25fc060ddace1c0eec1027bf252e02a",
"search_probe.dll": "911beb9f6bb52390cde8a75562ca1d6c46f4629f641426a5250286903de60895",
"sqlite_inject.js": "61a5d8a9365353ee19ba82d95ded364331e7e69f6705b470c910d6560a22397a"
"math_probe.dll": "bdcf0449d35102963c35f0f8ba985ffe3e5f83c083da5587b4978c82911c555f",
"search_probe.dll": "f65fc87dff2b3b4b81a46c227659e652e091bfec7f15fff5a1c85c3e10d3a9ef",
"sqlite_inject.js": "61a5d8a9365353ee19ba82d95ded364331e7e69f6705b470c910d6560a22397a",
"webhook_listener.js": "08fe2a99996cd3ee91d4bc123cc6175d16527224d10ba041a007b0fc68eed880",
"webhook_probe.dll": "10acf825aa3c37f356bff6639534db192a5e9babb0402e5d0651f8aecf8c2101"
}
}