registry  /  @bobfrankston/rmfmail  /  1.2.118

@bobfrankston/rmfmail@1.2.118

Local-first email client with IMAP sync and standalone native app

AI Security Review

scanned 2h ago · by lpm-firewall-ai

Android runtime code transmits account metadata and diagnostic messages to an unrelated remote logging host. The request is silent and can occur while accounts are registered or synchronized.

Static reason
One or more suspicious static signals were detected.; previous stored version diff introduced dangerous source
Trigger
Starting the Android client and adding or synchronizing a mail account.
Impact
Exfiltrates email addresses, account identifiers, IMAP host details, authentication mode, and potentially error content.
Mechanism
silent remote diagnostic logging of account-derived data
Attack narrative
At Android runtime, `vlog` sends up to 800 characters of messages to `rmf39.aaz.lt` with no user-facing consent. Calls log account IDs, email addresses, IMAP host/port, authentication type, sync state, and caught error messages. The same endpoint exists in the worker entrypoint, establishing a concrete background data-exfiltration path unrelated to required mail-provider operations.
Rationale
The package contains a concrete, silent runtime exfiltration path for sensitive mail-account metadata to a non-provider endpoint. Local lifecycle and launcher behavior do not mitigate that behavior.
Evidence
packages/mailx-store-web/android-bootstrap.tspackages/mailx-store-web/worker-entry.tsbin/postinstall.jsbin/mailx.jsbin/rmfmailto-src/src/main.rs
Network endpoints1
rmf39.aaz.lt/logit/{encoded-message}?log=mailx-android&silent=true

Decision evidence

public snapshot
AI called this Malicious at 94.0% confidence as Malware with low false-positive risk.
Evidence for block
  • `packages/mailx-store-web/android-bootstrap.ts` silently sends logs to `rmf39.aaz.lt`.
  • Logs include account ID, email, IMAP host/port, auth type, and errors.
  • The telemetry runs during Android account setup/sync, not an explicit diagnostic command.
  • `worker-entry.ts` contains the same silent remote logging endpoint.
Evidence against
  • `bin/postinstall.js` only checks Node version and creates local workspace symlinks.
  • `bin/mailx.js` update/install behavior is behind runtime UI actions.
  • `bin/rmfmailto-src/src/main.rs` is a mailto launcher with available source.
Behavioral surface
Source
ChildProcessCryptoDynamicRequireEnvironmentVarsEvalFilesystemNetworkShellWebSocket
Supply chain
HighEntropyStringsMinifiedObfuscatedProtestwareTelemetryUrlStrings
ManifestNo manifest risk signals triggered.
scanned 415 file(s), 16.1 MB of source, external domains: 127.0.0.1, accounts.google.com, api.anthropic.com, api.openai.com, autoconfig.thunderbird.net, bugs.webkit.org, bugzilla.mozilla.org, calendar.google.com, cdn.jsdelivr.net, cdnjs.cloudflare.com, contacts.google.com, cure53.de, developer.mozilla.org, developers.google.com, docs.microsoft.com, drafts.csswg.org, en.cppreference.com, feross.org, github.com, gmail.googleapis.com, graph.microsoft.com, html.spec.whatwg.org, lea.verou.me, mail.google.com, oauth2.googleapis.com, openjdk.java.net, opensource.org, people.googleapis.com, quilljs.com, rmf39.aaz.lt, tasks.google.com, tasks.googleapis.com, tiptap.dev, translate.google.com, websemantics.uk, www.cse.yorku.ca, www.googleapis.com, www.tiny.cloud, www.w3.org

Source & flagged code

13 flagged · loading source
package.jsonView file
scripts.postinstall = node bin/postinstall.js
High
Install Time Lifecycle Scripts

Package defines install-time lifecycle scripts.

package.jsonView on unpkg
scripts.postinstall = node bin/postinstall.js
Medium
Ambiguous Install Lifecycle Script

Install-time lifecycle script is not statically allowlisted and needs review.

package.jsonView on unpkg
bin/mailx.jsView file
54import net from "node:net"; L55: import { execSync } from "node:child_process"; L56: import { ports } from "@bobfrankston/miscinfo";
High
Child Process

Package source references child process execution.

bin/mailx.jsView on unpkg · L54
268// reg.exe value for `\"path\" \"%1\"` — double-backslash escapes for L269: // cmd.exe's shell layer, which strips one level before the value reaches L270: // reg.exe. Quoting %1 keeps `?` and `&` from being eaten by cmd before
High
Shell

Package source references shell execution.

bin/mailx.jsView on unpkg · L268
53Cross-file remote execution chain: bin/mailx.js spawns bin/popout-server.js; helper contains network access plus dynamic code execution. L53: import os from "node:os"; L54: import net from "node:net"; L55: import { execSync } from "node:child_process"; L56: import { ports } from "@bobfrankston/miscinfo"; ... L185: // Linux pickers read Name= from the .desktop file directly. L186: if (process.platform === "linux") { L187: const appsDir = path.join(os.homedir(), ".local", "share", "applications"); L188: const desktopPath = path.join(appsDir, "rmfmail.desktop"); ... L246: // (shipped in the npm package) to the per-app location below. L247: const __perAppBin = path.join(process.env.LOCALAPPDATA || "", "rmfmail", "bin"); L248: const __rmfmailtoExe = path.join(__perAppBin, "rmfmailto.exe"); ... L268: // reg.exe value for `\"path\" \"%1\"` — double-backslash escapes for
High
Cross File Remote Execution Context

Source spawns a local helper that also contains network and dynamic execution context; review data flow before blocking.

bin/mailx.jsView on unpkg · L53
2336clearInstanceFile(); L2337: const { spawn: spawnChild } = await import("child_process"); L2338: const child = spawnChild("rmfmail", [], { detached: true, stdio: "ignore", shell: true, windowsHide: true }); ... L2351: } L2352: // Auto-update action: run npm install then restart L2353: if (req._action === "performUpdate") {
High
Runtime Package Install

Package source invokes a package manager install command at runtime.

bin/mailx.jsView on unpkg · L2336
53import os from "node:os"; L54: import net from "node:net"; L55: import { execSync } from "node:child_process"; L56: import { ports } from "@bobfrankston/miscinfo"; ... L185: // Linux pickers read Name= from the .desktop file directly. L186: if (process.platform === "linux") { L187: const appsDir = path.join(os.homedir(), ".local", "share", "applications"); L188: const desktopPath = path.join(appsDir, "rmfmail.desktop"); ... L246: // (shipped in the npm package) to the per-app location below. L247: const __perAppBin = path.join(process.env.LOCALAPPDATA || "", "rmfmail", "bin"); L248: const __rmfmailtoExe = path.join(__perAppBin, "rmfmailto.exe"); ... L268: // reg.exe value for `\"path\" \"%1\"` — double-backslash escapes for
Medium
Install Persistence

Source writes installer persistence such as shell profile or service configuration.

bin/mailx.jsView on unpkg · L53
client/lib/quill/quill.jsView file
1/*! For license information please see quill.js.LICENSE.txt */ L2: !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Quill=e():t.... L3: //# sourceMappingURL=quill.js.map
Low
Eval

Package source references a known benign dynamic code generation pattern.

client/lib/quill/quill.jsView on unpkg · L1
bin/lean-accounts.jsView file
137package = @bobfrankston/rmfmail; repositoryIdentity = mailx; dependency = jsonc-parser L137: const mod = await import(pathToFileURL(path.join(dir, "lib", "esm", "main.js")).href).catch(() => null) || L138: await import("jsonc-parser").catch(() => null); L139: if (mod) { parseJsonc = mod.parse; break; }
High
Copied Package Dependency Bridge

Package metadata claims a different repository identity while copied source loads a runtime dependency bridge.

bin/lean-accounts.jsView on unpkg · L137
136if (fs.existsSync(path.join(dir, "package.json"))) { L137: const mod = await import(pathToFileURL(path.join(dir, "lib", "esm", "main.js")).href).catch(() => null) || L138: await import("jsonc-parser").catch(() => null);
Medium
Dynamic Require

Package source references dynamic require/import behavior.

bin/lean-accounts.jsView on unpkg · L136
bin/build-bundles.mjsView file
5* mailx's msger custom protocol routes every fetch through one IPC channel L6: * (Rust → daemon stdin → response → stdout), so an ES module import cascade L7: * pays a multi-second roundtrip per file. With ~17 client-side modules each ... L148: logLevel: "info", L149: // .wasm files are inlined as base64 data URLs so the runtime can L150: // decode → Uint8Array → pass as initSqlJs({ wasmBinary }), bypassing L151: // sql.js's fetch(...) of the .wasm. Android WebView refuses to L152: // compile WASM fetched from file:// (even with AllowFileAccess /
Low
Weak Crypto

Package source references weak cryptographic algorithms.

bin/build-bundles.mjsView on unpkg · L5
bin/rmfmailto.exeView file
path = bin/rmfmailto.exe kind = native_binary sizeBytes = 310272 magicHex = [redacted]
Medium
Ships Native Binary

Package ships native binary artifacts.

bin/rmfmailto.exeView on unpkg
packages/mailx-service/index.jsView file
matchType = previous_version_dangerous_delta matchedPackage = @bobfrankston/rmfmail@1.2.116 matchedIdentity = npm:QGJvYmZyYW5rc3Rvbi9ybWZtYWls:1.2.116 similarity = 0.917 summary = stored previous version shares package body but lacks this dangerous source file
Critical
Previous Version Dangerous Delta

This package version adds a dangerous source file absent from the previous stored version; route for source-aware review.

packages/mailx-service/index.jsView on unpkg

Findings

1 Critical6 High8 Medium8 Low
CriticalPrevious Version Dangerous Deltapackages/mailx-service/index.js
HighInstall Time Lifecycle Scriptspackage.json
HighChild Processbin/mailx.js
HighShellbin/mailx.js
HighCopied Package Dependency Bridgebin/lean-accounts.js
HighCross File Remote Execution Contextbin/mailx.js
HighRuntime Package Installbin/mailx.js
MediumAmbiguous Install Lifecycle Scriptpackage.json
MediumDynamic Requirebin/lean-accounts.js
MediumNetwork
MediumEnvironment Vars
MediumInstall Persistencebin/mailx.js
MediumProtestware
MediumShips Native Binarybin/rmfmailto.exe
MediumStructural Risk Force Deep Review
LowScripts Present
LowEvalclient/lib/quill/quill.js
LowWeak Cryptobin/build-bundles.mjs
LowFilesystem
LowObfuscated
LowHigh Entropy Strings
LowTelemetry
LowUrl Strings