Files
smartmta/dist_ts/security/classes.rustsecuritybridge.js

145 lines
10 KiB
JavaScript

import * as plugins from '../plugins.js';
import * as paths from '../paths.js';
import { logger } from '../logger.js';
// ---------------------------------------------------------------------------
// RustSecurityBridge — singleton wrapper around smartrust.RustBridge
// ---------------------------------------------------------------------------
/**
* Bridge between TypeScript and the Rust `mailer-bin` binary.
*
* Uses `@push.rocks/smartrust` for JSON-over-stdin/stdout IPC.
* Singleton — access via `RustSecurityBridge.getInstance()`.
*/
export class RustSecurityBridge {
static instance = null;
bridge;
_running = false;
constructor() {
this.bridge = new plugins.smartrust.RustBridge({
binaryName: 'mailer-bin',
cliArgs: ['--management'],
requestTimeoutMs: 30_000,
readyTimeoutMs: 10_000,
localPaths: [
plugins.path.join(paths.packageDir, 'dist_rust', 'mailer-bin'),
plugins.path.join(paths.packageDir, 'rust', 'target', 'release', 'mailer-bin'),
plugins.path.join(paths.packageDir, 'rust', 'target', 'debug', 'mailer-bin'),
],
searchSystemPath: false,
});
// Forward lifecycle events
this.bridge.on('ready', () => {
this._running = true;
logger.log('info', 'Rust security bridge is ready');
});
this.bridge.on('exit', (code, signal) => {
this._running = false;
logger.log('warn', `Rust security bridge exited (code=${code}, signal=${signal})`);
});
this.bridge.on('stderr', (line) => {
logger.log('debug', `[rust-bridge] ${line}`);
});
}
/** Get or create the singleton instance. */
static getInstance() {
if (!RustSecurityBridge.instance) {
RustSecurityBridge.instance = new RustSecurityBridge();
}
return RustSecurityBridge.instance;
}
/** Whether the Rust process is currently running and accepting commands. */
get running() {
return this._running;
}
// -----------------------------------------------------------------------
// Lifecycle
// -----------------------------------------------------------------------
/**
* Spawn the Rust binary and wait for the ready signal.
* @returns `true` if the binary started successfully, `false` otherwise.
*/
async start() {
if (this._running) {
return true;
}
try {
const ok = await this.bridge.spawn();
this._running = ok;
if (ok) {
logger.log('info', 'Rust security bridge started');
}
else {
logger.log('warn', 'Rust security bridge failed to start (binary not found or timeout)');
}
return ok;
}
catch (err) {
logger.log('error', `Failed to start Rust security bridge: ${err.message}`);
return false;
}
}
/** Kill the Rust process. */
async stop() {
if (!this._running) {
return;
}
try {
this.bridge.kill();
this._running = false;
logger.log('info', 'Rust security bridge stopped');
}
catch (err) {
logger.log('error', `Error stopping Rust security bridge: ${err.message}`);
}
}
// -----------------------------------------------------------------------
// Commands — thin typed wrappers over sendCommand
// -----------------------------------------------------------------------
/** Ping the Rust process. */
async ping() {
const res = await this.bridge.sendCommand('ping', {});
return res?.pong === true;
}
/** Get version information for all Rust crates. */
async getVersion() {
return this.bridge.sendCommand('version', {});
}
/** Validate an email address. */
async validateEmail(email) {
return this.bridge.sendCommand('validateEmail', { email });
}
/** Detect bounce type from SMTP response / diagnostic code. */
async detectBounce(opts) {
return this.bridge.sendCommand('detectBounce', opts);
}
/** Scan email content for threats (phishing, spam, malware, etc.). */
async scanContent(opts) {
return this.bridge.sendCommand('scanContent', opts);
}
/** Check IP reputation via DNSBL. */
async checkIpReputation(ip) {
return this.bridge.sendCommand('checkIpReputation', { ip });
}
/** Verify DKIM signatures on a raw email message. */
async verifyDkim(rawMessage) {
return this.bridge.sendCommand('verifyDkim', { rawMessage });
}
/** Sign an email with DKIM. */
async signDkim(opts) {
return this.bridge.sendCommand('signDkim', opts);
}
/** Check SPF for a sender. */
async checkSpf(opts) {
return this.bridge.sendCommand('checkSpf', opts);
}
/**
* Compound email security verification: DKIM + SPF + DMARC in one IPC call.
*
* This is the preferred method for inbound email verification — it avoids
* 3 sequential round-trips and correctly passes raw mail-auth types internally.
*/
async verifyEmail(opts) {
return this.bridge.sendCommand('verifyEmail', opts);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ydXN0c2VjdXJpdHlicmlkZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9zZWN1cml0eS9jbGFzc2VzLnJ1c3RzZWN1cml0eWJyaWRnZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEtBQUssS0FBSyxNQUFNLGFBQWEsQ0FBQztBQUNyQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBa0l0Qyw4RUFBOEU7QUFDOUUscUVBQXFFO0FBQ3JFLDhFQUE4RTtBQUU5RTs7Ozs7R0FLRztBQUNILE1BQU0sT0FBTyxrQkFBa0I7SUFDckIsTUFBTSxDQUFDLFFBQVEsR0FBOEIsSUFBSSxDQUFDO0lBRWxELE1BQU0sQ0FBcUU7SUFDM0UsUUFBUSxHQUFHLEtBQUssQ0FBQztJQUV6QjtRQUNFLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxPQUFPLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBa0I7WUFDOUQsVUFBVSxFQUFFLFlBQVk7WUFDeEIsT0FBTyxFQUFFLENBQUMsY0FBYyxDQUFDO1lBQ3pCLGdCQUFnQixFQUFFLE1BQU07WUFDeEIsY0FBYyxFQUFFLE1BQU07WUFDdEIsVUFBVSxFQUFFO2dCQUNWLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLEVBQUUsV0FBVyxFQUFFLFlBQVksQ0FBQztnQkFDOUQsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxZQUFZLENBQUM7Z0JBQzlFLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsWUFBWSxDQUFDO2FBQzdFO1lBQ0QsZ0JBQWdCLEVBQUUsS0FBSztT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